Merge branch 'dev' of lab.si.usi.ch:sa4-2020/the-sanmarinoes/backend into ci-cd
This commit is contained in:
commit
92b799e61c
71 changed files with 1623 additions and 581 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -136,3 +136,6 @@ gradle-app.setting
|
|||
|
||||
# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898
|
||||
# gradle/wrapper/gradle-wrapper.properties
|
||||
|
||||
# IntelliJ
|
||||
*.iml
|
|
@ -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
|
||||
|
|
12
backend.iml
12
backend.iml
|
@ -1,12 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<excludeFolder url="file://$MODULE_DIR$/.gradle" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
|
@ -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'
|
||||
|
|
39
gradle.yml
Normal file
39
gradle.yml
Normal file
|
@ -0,0 +1,39 @@
|
|||
# vim: set ts=2 sw=2 et tw=80:
|
||||
image: gradle:jdk13
|
||||
|
||||
stages:
|
||||
- build
|
||||
- test
|
||||
- deploy
|
||||
|
||||
smarthut_build:
|
||||
stage: build
|
||||
script:
|
||||
- gradle assemble
|
||||
artifacts:
|
||||
paths:
|
||||
- build/libs/*.jar
|
||||
expire_in: 1 week
|
||||
|
||||
smarthut_test:
|
||||
stage: test
|
||||
script:
|
||||
- gradle check
|
||||
|
||||
smarthut_deploy:
|
||||
stage: deploy
|
||||
image: docker:latest
|
||||
services:
|
||||
- docker:dind
|
||||
variables:
|
||||
DOCKER_DRIVER: overlay
|
||||
before_script:
|
||||
- docker version
|
||||
- docker info
|
||||
- docker login -u smarthutsm -p $CI_DOCKER_PASS
|
||||
script:
|
||||
- "docker build -t smarthutsm/smarthut:${CI_COMMIT_BRANCH} --pull ."
|
||||
- "docker push smarthutsm/smarthut:${CI_COMMIT_BRANCH}"
|
||||
after_script:
|
||||
- docker logout
|
||||
|
40
socket_test.html
Normal file
40
socket_test.html
Normal file
|
@ -0,0 +1,40 @@
|
|||
<!-- vim: set ts=2 sw=2 et tw=80: -->
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
<body>
|
||||
<div id="malusa">
|
||||
<h1>Waiting for authentication...</h1>
|
||||
</div>
|
||||
<script>
|
||||
let malusa = document.getElementById("malusa");
|
||||
let connection = new WebSocket("ws://localhost:8080/sensor-socket");
|
||||
console.log("***CREATED WEBSOCKET");
|
||||
|
||||
connection.onopen = function(evt) {
|
||||
console.log("***ONOPEN", evt);
|
||||
connection.send(JSON.stringify({token: prompt("insert authentication token")}));
|
||||
};
|
||||
|
||||
connection.onmessage = function(evt) {
|
||||
console.log("***ONMESSAGE", evt);
|
||||
let data = JSON.parse(evt.data);
|
||||
|
||||
if (data.authenticated) {
|
||||
malusa.innerHTML = "<h1>Socket is now authenticated!</h1>" +
|
||||
"<img src='https://maggioni.xyz/astley.gif'>";
|
||||
} else if (data.authenticated === false) {
|
||||
malusa.innerHTML = "<h1>Authentication error</h1>";
|
||||
} else {
|
||||
malusa.innerHTML += "<p><pre>" + JSON.stringify(JSON.parse(evt.data), null, 2) + "</pre></p>";
|
||||
}
|
||||
};
|
||||
|
||||
connection.onerror = function(evt) {
|
||||
console.error("***ONERROR", evt);
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -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) {
|
||||
|
|
|
@ -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<Json> {
|
|||
return JsonParser.parseString(json.value());
|
||||
}
|
||||
}
|
||||
|
||||
/** GSON exclusion strategy to exclude attributes with @GsonExclude */
|
||||
class AnnotationExclusionStrategy implements ExclusionStrategy {
|
||||
@Override
|
||||
public boolean shouldSkipField(FieldAttributes f) {
|
||||
return f.getAnnotation(GsonExclude.class) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldSkipClass(Class<?> clazz) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
package ch.usi.inf.sa4.sanmarinoes.smarthut.config;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
public @interface GsonExclude {}
|
|
@ -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());
|
||||
|
|
|
@ -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> T getClaimFromToken(String token, Function<Claims, T> 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<String, Object> 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<String, Object> 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));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
package ch.usi.inf.sa4.sanmarinoes.smarthut.config;
|
||||
|
||||
import io.jsonwebtoken.Claims;
|
||||
import io.jsonwebtoken.Jwts;
|
||||
import io.jsonwebtoken.SignatureAlgorithm;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.function.Function;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/** A utility class to handle JWTs */
|
||||
@Component
|
||||
public class JWTTokenUtils {
|
||||
/** The duration in seconds of the validity of a single token */
|
||||
private static final long JWT_TOKEN_VALIDITY = 5 * 60 * 60;
|
||||
|
||||
/** The secret key used to encrypt all JWTs */
|
||||
@Value("${jwt.secret}")
|
||||
private String secret;
|
||||
|
||||
/**
|
||||
* Retrieves the claimed username from a given token
|
||||
*
|
||||
* @param token the token to inspect
|
||||
* @return the username
|
||||
*/
|
||||
public String getUsernameFromToken(String token) {
|
||||
return getClaimFromToken(token, Claims::getSubject);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the token given is expired or not
|
||||
*
|
||||
* @param token the given token
|
||||
* @return true if expired, false if not
|
||||
*/
|
||||
public Boolean isTokenExpired(String token) {
|
||||
final Date expiration = getClaimFromToken(token, Claims::getExpiration);
|
||||
return expiration.before(new Date());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new JWT for a given user. While creating the token - 1. Define claims of the token,
|
||||
* like Issuer, Expiration, Subject, and the ID 2. Sign the JWT using the HS512 algorithm and
|
||||
* secret key. 3. According to JWS Compact Serialization
|
||||
* (https://tools.ietf.org/html/draft-ietf-jose-json-web-signature-41#section-3.1) compaction of
|
||||
* the JWT to a URL-safe string
|
||||
*
|
||||
* @param user the user to which create a JWT
|
||||
* @return the newly generated token
|
||||
*/
|
||||
public String generateToken(UserDetails user) {
|
||||
return Jwts.builder()
|
||||
.setClaims(new HashMap<>())
|
||||
.setSubject(user.getUsername())
|
||||
.setIssuedAt(new Date(System.currentTimeMillis()))
|
||||
.setExpiration(new Date(System.currentTimeMillis() + JWT_TOKEN_VALIDITY * 1000))
|
||||
.signWith(SignatureAlgorithm.HS512, secret)
|
||||
.compact();
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the token given against matching userDetails
|
||||
*
|
||||
* @param token the token given
|
||||
* @param userDetails user details to validate against
|
||||
* @return true if valid, false if not
|
||||
*/
|
||||
public Boolean validateToken(String token, UserDetails userDetails) {
|
||||
final String username = getUsernameFromToken(token);
|
||||
return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
|
||||
}
|
||||
|
||||
private <T> T getClaimFromToken(String token, Function<Claims, T> claimsResolver) {
|
||||
final Claims claims = getAllClaimsFromToken(token);
|
||||
return claimsResolver.apply(claims);
|
||||
}
|
||||
|
||||
private Claims getAllClaimsFromToken(String token) {
|
||||
return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
|
||||
}
|
||||
}
|
|
@ -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<String> regexPredicate(final String regex) {
|
||||
return regex(regex)::apply;
|
||||
private SecurityContext securityContext() {
|
||||
return SecurityContext.builder()
|
||||
.securityReferences(defaultAuth())
|
||||
.forPaths(authenticatedPaths()::test)
|
||||
.build();
|
||||
}
|
||||
|
||||
private List<SecurityReference> defaultAuth() {
|
||||
final AuthorizationScope authorizationScope =
|
||||
new AuthorizationScope("global", "accessEverything");
|
||||
return List.of(
|
||||
new SecurityReference("Bearer", new AuthorizationScope[] {authorizationScope}));
|
||||
}
|
||||
|
||||
private Predicate<String> authenticatedPaths() {
|
||||
return ((Predicate<String>) PathSelectors.regex("/auth/update")::apply)
|
||||
.or(PathSelectors.regex("/room.*")::apply)
|
||||
.or(PathSelectors.regex("/device.*")::apply)
|
||||
.or(PathSelectors.regex("/buttonDimmer.*")::apply)
|
||||
.or(PathSelectors.regex("/dimmableLight.*")::apply)
|
||||
.or(PathSelectors.regex("/knobDimmer.*")::apply)
|
||||
.or(PathSelectors.regex("/regularLight.*")::apply)
|
||||
.or(PathSelectors.regex("/sensor.*")::apply)
|
||||
.or(PathSelectors.regex("/smartPlug.*")::apply)
|
||||
.or(PathSelectors.regex("/switch.*")::apply)
|
||||
.or(PathSelectors.regex("/motionSensor.*")::apply);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -51,6 +51,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
|
|||
// dont authenticate this particular request
|
||||
.authorizeRequests()
|
||||
.antMatchers(
|
||||
"/sensor-socket",
|
||||
"/auth/login",
|
||||
"/swagger-ui.html",
|
||||
"/register",
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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<ButtonDimmer, DimmableLight> {
|
||||
private ButtonDimmerRepository buttonDimmerRepository;
|
||||
private DimmableLightRepository dimmableLightRepository;
|
||||
|
||||
@Autowired
|
||||
protected ButtonDimmerController(
|
||||
ButtonDimmerRepository inputRepository, DimmableLightRepository outputRepository) {
|
||||
super(
|
||||
inputRepository,
|
||||
outputRepository,
|
||||
DimmableLight.BUTTON_DIMMER_DIMMABLE_LIGHT_CONNECTOR);
|
||||
this.buttonDimmerRepository = inputRepository;
|
||||
this.dimmableLightRepository = outputRepository;
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public List<ButtonDimmer> findAll() {
|
||||
return toList(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<DimmableLight> 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<Device> deviceRepository;
|
||||
@Autowired private RoomRepository roomRepository;
|
||||
|
||||
@GetMapping
|
||||
public List<Device> getAll(final Principal user) {
|
||||
return deviceRepository.findAllByUsername(user.getName());
|
||||
}
|
||||
|
||||
@PutMapping
|
||||
public Device update(@Valid @RequestBody DeviceSaveRequest deviceSaveRequest)
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -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}")
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
package ch.usi.inf.sa4.sanmarinoes.smarthut.controller;
|
||||
|
||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.error.NotFoundException;
|
||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* An abstract controller for an input device that has output connected to it. Aids to create the
|
||||
* output add and output remove route
|
||||
*
|
||||
* @param <I> the type of device this controller is for
|
||||
* @param <O> the output device attached to I
|
||||
*/
|
||||
public abstract class InputDeviceConnectionController<
|
||||
I extends InputDevice, O extends OutputDevice> {
|
||||
|
||||
private class IOPair {
|
||||
private final I input;
|
||||
private final O output;
|
||||
|
||||
private IOPair(I input, O output) {
|
||||
this.input = input;
|
||||
this.output = output;
|
||||
}
|
||||
}
|
||||
|
||||
private DeviceRepository<I> inputRepository;
|
||||
|
||||
private DeviceRepository<O> outputReposiory;
|
||||
|
||||
private Connector<I, O> connector;
|
||||
|
||||
/**
|
||||
* Contstructs the controller by requiring essential object for the controller implementation
|
||||
*
|
||||
* @param inputRepository the input device repository
|
||||
* @param outputRepository the output device repository
|
||||
* @param connector a appropriate Connector instance for the I and O tyoes.
|
||||
*/
|
||||
protected InputDeviceConnectionController(
|
||||
DeviceRepository<I> inputRepository,
|
||||
DeviceRepository<O> outputRepository,
|
||||
Connector<I, O> connector) {
|
||||
this.inputRepository = inputRepository;
|
||||
this.outputReposiory = outputRepository;
|
||||
this.connector = connector;
|
||||
}
|
||||
|
||||
private IOPair checkConnectionIDs(Long inputId, Long outputId) throws NotFoundException {
|
||||
final I input =
|
||||
inputRepository
|
||||
.findById(inputId)
|
||||
.orElseThrow(() -> new NotFoundException("input device"));
|
||||
final O output =
|
||||
outputReposiory
|
||||
.findById(outputId)
|
||||
.orElseThrow(() -> new NotFoundException("output device"));
|
||||
return new IOPair(input, output);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements the output device connection creation (add) route
|
||||
*
|
||||
* @param inputId input device id
|
||||
* @param outputId output device id
|
||||
* @return the list of output devices attached to the input device of id inputId
|
||||
* @throws NotFoundException if inputId or outputId are not valid
|
||||
*/
|
||||
protected Set<? extends OutputDevice> addOutput(Long inputId, Long outputId)
|
||||
throws NotFoundException {
|
||||
final IOPair pair = checkConnectionIDs(inputId, outputId);
|
||||
connector.connect(pair.input, pair.output, true);
|
||||
outputReposiory.save(pair.output);
|
||||
return pair.input.getOutputs();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements the output device connection destruction (remove) route
|
||||
*
|
||||
* @param inputId input device id
|
||||
* @param outputId output device id
|
||||
* @return the list of output devices attached to the input device of id inputId
|
||||
* @throws NotFoundException if inputId or outputId are not valid
|
||||
*/
|
||||
protected Set<? extends OutputDevice> removeOutput(Long inputId, Long outputId)
|
||||
throws NotFoundException {
|
||||
final IOPair pair = checkConnectionIDs(inputId, outputId);
|
||||
connector.connect(pair.input, pair.output, false);
|
||||
outputReposiory.save(pair.output);
|
||||
return pair.input.getOutputs();
|
||||
}
|
||||
}
|
|
@ -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<KnobDimmer, DimmableLight> {
|
||||
|
||||
@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<KnobDimmer> 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<DimmableLight> 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<MotionSensor> 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}")
|
||||
|
|
|
@ -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}")
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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<Sensor> 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}")
|
||||
|
|
|
@ -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}")
|
||||
|
|
|
@ -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<Switch, Switchable> {
|
||||
|
||||
@Autowired private SwitchRepository switchRepository;
|
||||
private SwitchRepository switchRepository;
|
||||
private SwitchableRepository<Switchable> switchableRepository;
|
||||
|
||||
/**
|
||||
* Contstructs the controller by requiring essential object for the controller implementation
|
||||
*
|
||||
* @param inputRepository the input device repository
|
||||
* @param outputRepository the output device repository
|
||||
*/
|
||||
@Autowired
|
||||
protected SwitchController(
|
||||
SwitchRepository inputRepository, SwitchableRepository<Switchable> outputRepository) {
|
||||
super(inputRepository, outputRepository, Switchable.SWITCH_SWITCHABLE_CONNECTOR);
|
||||
this.switchRepository = inputRepository;
|
||||
this.switchableRepository = outputRepository;
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public List<Switch> findAll() {
|
||||
|
@ -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<Switchable> 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}")
|
||||
|
|
|
@ -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<Void> testConnection() {
|
||||
return ResponseEntity.ok(null);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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<DimmableLight> lights = new HashSet<DimmableLight>();
|
||||
|
||||
/** 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<DimmableLight> getLights() {
|
||||
return this.lights;
|
||||
}
|
||||
|
||||
public void setLights(Set<DimmableLight> 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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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<DimmableLight> lights = new HashSet<DimmableLight>();
|
||||
|
||||
/** 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<DimmableLight> lights) {
|
||||
this.lights = lights;
|
||||
}
|
||||
|
||||
public Set<DimmableLight> getLights() {
|
||||
return lights;
|
||||
}
|
||||
}
|
|
@ -45,4 +45,8 @@ public class RegularLightSaveRequest {
|
|||
public void setOn(boolean on) {
|
||||
this.on = on;
|
||||
}
|
||||
|
||||
public void setId(long id) {
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,4 +45,8 @@ public class SmartPlugSaveRequest {
|
|||
public void setOn(boolean on) {
|
||||
this.on = on;
|
||||
}
|
||||
|
||||
public void setId(long id) {
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
package ch.usi.inf.sa4.sanmarinoes.smarthut.dto;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/** An on/off/toggle operation on a switch */
|
||||
public class SwitchOperationRequest {
|
||||
|
||||
/** The device id */
|
||||
@NotNull private Long id;
|
||||
|
||||
public enum OperationType {
|
||||
ON,
|
||||
OFF,
|
||||
TOGGLE
|
||||
}
|
||||
|
||||
/** The type of switch operation */
|
||||
@NotNull private SwitchOperationRequest.OperationType type;
|
||||
|
||||
public OperationType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(OperationType type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,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);
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<DimmableLight> 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<DimmableLight> getLights() {
|
||||
return this.lights;
|
||||
}
|
||||
|
||||
public void setLights(Set<DimmableLight> newLights) {
|
||||
this.lights = newLights;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,8 @@ import org.springframework.data.repository.CrudRepository;
|
|||
public interface ConfirmationTokenRepository extends CrudRepository<ConfirmationToken, String> {
|
||||
ConfirmationToken findByConfirmationToken(String confirmationToken);
|
||||
|
||||
ConfirmationToken findByUser(User user);
|
||||
|
||||
@Transactional
|
||||
void deleteByUserAndResetPassword(User user, boolean resetPassword);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* A rule on how to connect an input device type to an output device type
|
||||
*
|
||||
* @param <I> the input device type
|
||||
* @param <O> the output device type
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface Connector<I extends InputDevice, O extends OutputDevice> {
|
||||
|
||||
/**
|
||||
* Connects or disconnects input to output
|
||||
*
|
||||
* @param input the input device
|
||||
* @param output the output device
|
||||
* @param connect true if connection, false if disconnection
|
||||
*/
|
||||
void connect(I input, O output, boolean connect);
|
||||
|
||||
/**
|
||||
* Produces a basic implementation of a connector, assuming there is a OneToMany relationship
|
||||
* between J and K
|
||||
*
|
||||
* @param outputsGetter the getter method of the set of outputs on the input class
|
||||
* @param inputSetter the setter method for the input id on the output class
|
||||
* @param <J> the input device type
|
||||
* @param <K> the output device type
|
||||
* @return a Connector implementation for the pair of types J and K
|
||||
*/
|
||||
static <J extends InputDevice, K extends OutputDevice> Connector<J, K> basic(
|
||||
Function<J, Set<? super K>> outputsGetter, BiConsumer<K, Long> inputSetter) {
|
||||
return (i, o, connect) -> {
|
||||
if (connect) {
|
||||
outputsGetter.apply(i).add(o);
|
||||
} else {
|
||||
outputsGetter.apply(i).remove(o);
|
||||
}
|
||||
|
||||
inputSetter.accept(o, connect ? i.getId() : null);
|
||||
};
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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<T extends Device> extends CrudRepository<T, Long> {
|
||||
List<T> findByRoomId(@Param("roomId") long roomId);
|
||||
|
||||
/**
|
||||
* Finds devices by their id and a username
|
||||
*
|
||||
* @param id the device id
|
||||
* @param username a User's username
|
||||
* @return an optional device, empty if none found
|
||||
*/
|
||||
@Query("SELECT d FROM Device d JOIN d.room r JOIN r.user u WHERE d.id = ?1 AND u.username = ?2")
|
||||
Optional<T> findByIdAndUsername(Long id, String username);
|
||||
|
||||
/**
|
||||
* Finds all devices belonging to a user
|
||||
*
|
||||
* @param username the User's username
|
||||
* @return all devices of that user
|
||||
*/
|
||||
@Query("SELECT d FROM Device d JOIN d.room r JOIN r.user u WHERE u.username = ?1")
|
||||
List<T> findAllByUsername(String username);
|
||||
|
||||
/**
|
||||
* Find the user associated with a device through a room
|
||||
*
|
||||
* @param deviceId the device id
|
||||
* @return a user object
|
||||
*/
|
||||
@Query("SELECT u FROM Device d JOIN d.room r JOIN r.user u WHERE d.id = ?1")
|
||||
User findUser(Long deviceId);
|
||||
}
|
||||
|
|
|
@ -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<ButtonDimmer, DimmableLight>
|
||||
BUTTON_DIMMER_DIMMABLE_LIGHT_CONNECTOR =
|
||||
Connector.basic(ButtonDimmer::getOutputs, DimmableLight::setDimmerId);
|
||||
|
||||
public static final Connector<KnobDimmer, DimmableLight> KNOB_DIMMER_DIMMABLE_LIGHT_CONNECTOR =
|
||||
Connector.basic(KnobDimmer::getOutputs, DimmableLight::setDimmerId);
|
||||
|
||||
public DimmableLight() {
|
||||
super("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;
|
||||
}
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
|
||||
|
||||
public interface DimmableLightRepository extends DeviceRepository<DimmableLight> {}
|
||||
public interface DimmableLightRepository extends SwitchableRepository<DimmableLight> {}
|
||||
|
|
|
@ -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<DimmableLight> lights;
|
||||
|
||||
/**
|
||||
* Get the lights connected to this dimmer
|
||||
*
|
||||
* @return duh
|
||||
*/
|
||||
@Override
|
||||
public Set<DimmableLight> getOutputs() {
|
||||
return this.lights;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<DimmableLight> 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<DimmableLight> lights) {
|
||||
this.lights = lights;
|
||||
}
|
||||
|
||||
public Set<DimmableLight> getLights() {
|
||||
return lights;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
|
||||
|
||||
public interface RegularLightRepository extends DeviceRepository<RegularLight> {}
|
||||
public interface RegularLightRepository extends SwitchableRepository<RegularLight> {}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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<SensorType, BigDecimal> TYPICAL_VALUES =
|
||||
Map.of(
|
||||
SensorType.TEMPERATURE, new BigDecimal(17.0),
|
||||
SensorType.HUMIDITY, new BigDecimal(40.0),
|
||||
SensorType.LIGHT, new BigDecimal(1000));
|
||||
|
||||
/** Type of sensor, i.e. of the thing the sensor measures. */
|
||||
public enum SensorType {
|
||||
/** A sensor that measures temperature in degrees celsius */
|
||||
|
@ -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 + '}';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
|
||||
|
||||
public interface SmartPlugRepository extends DeviceRepository<SmartPlug> {}
|
||||
public interface SmartPlugRepository extends SwitchableRepository<SmartPlug> {}
|
||||
|
|
|
@ -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<Switchable> 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<Switchable> getOutputs() {
|
||||
return switchables;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
|
||||
|
||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.config.GsonExclude;
|
||||
import javax.persistence.*;
|
||||
|
||||
/** A device that can be turned either on or off */
|
||||
@Entity
|
||||
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
|
||||
public abstract class Switchable extends OutputDevice {
|
||||
|
||||
public static final Connector<Switch, Switchable> SWITCH_SWITCHABLE_CONNECTOR =
|
||||
Connector.basic(Switch::getOutputs, Switchable::setSwitchId);
|
||||
|
||||
@ManyToOne
|
||||
@GsonExclude
|
||||
@JoinColumn(name = "switch_id", updatable = false, insertable = false)
|
||||
private Switch switchDevice;
|
||||
|
||||
@Column(name = "switch_id")
|
||||
private Long switchId;
|
||||
|
||||
protected Switchable(String kind) {
|
||||
super(kind);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the device is on (true) or not (false)
|
||||
*
|
||||
* @return whether the device is on (true) or not (false)
|
||||
*/
|
||||
public abstract boolean isOn();
|
||||
|
||||
/**
|
||||
* Sets the on status of the device
|
||||
*
|
||||
* @param on the new on status: true for on, false for off
|
||||
*/
|
||||
public abstract void setOn(boolean on);
|
||||
|
||||
public Long getSwitchId() {
|
||||
return switchId;
|
||||
}
|
||||
|
||||
public void setSwitchId(Long switchId) {
|
||||
this.switchId = switchId;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
|
||||
|
||||
/**
|
||||
* SwitchableRepository acts as a superclass for the other repositories so to mirror in the database
|
||||
* the class inheritance present among the various switchable devices.
|
||||
*/
|
||||
public interface SwitchableRepository<T extends Switchable> extends DeviceRepository<T> {}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
package ch.usi.inf.sa4.sanmarinoes.smarthut.socket;
|
||||
|
||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.config.GsonConfig;
|
||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.config.JWTTokenUtils;
|
||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.User;
|
||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.UserRepository;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonObject;
|
||||
import io.jsonwebtoken.ExpiredJwtException;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiConsumer;
|
||||
import javax.websocket.MessageHandler;
|
||||
import javax.websocket.Session;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/** Generates MessageHandlers for unauthenticated socket sessions */
|
||||
@Component
|
||||
public class AuthenticationMessageListener {
|
||||
|
||||
private Gson gson = GsonConfig.gson();
|
||||
|
||||
private JWTTokenUtils jwtTokenUtils;
|
||||
|
||||
private UserRepository userRepository;
|
||||
|
||||
@Autowired
|
||||
public AuthenticationMessageListener(
|
||||
JWTTokenUtils jwtTokenUtils, UserRepository userRepository) {
|
||||
this.jwtTokenUtils = jwtTokenUtils;
|
||||
this.userRepository = userRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a new message handler to handle socket authentication
|
||||
*
|
||||
* @param session the session to which authentication must be checked
|
||||
* @param authorizedSetter function to call once user is authenticated
|
||||
* @return a new message handler to handle socket authentication
|
||||
*/
|
||||
MessageHandler.Whole<String> newHandler(
|
||||
final Session session, BiConsumer<User, Session> authorizedSetter) {
|
||||
return new MessageHandler.Whole<>() {
|
||||
@Override
|
||||
public void onMessage(final String message) {
|
||||
if (message == null) {
|
||||
acknowledge(false);
|
||||
return;
|
||||
}
|
||||
|
||||
String token;
|
||||
String username;
|
||||
|
||||
try {
|
||||
token = gson.fromJson(message, JsonObject.class).get("token").getAsString();
|
||||
username = jwtTokenUtils.getUsernameFromToken(token);
|
||||
} catch (ExpiredJwtException e) {
|
||||
System.err.println(e.getMessage());
|
||||
acknowledge(false);
|
||||
return;
|
||||
} catch (Throwable ignored) {
|
||||
System.out.println("Token format not valid");
|
||||
acknowledge(false);
|
||||
return;
|
||||
}
|
||||
|
||||
final User user = userRepository.findByUsername(username);
|
||||
if (user == null || jwtTokenUtils.isTokenExpired(token)) {
|
||||
System.out.println("Token not valid");
|
||||
acknowledge(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Here user is authenticated
|
||||
session.removeMessageHandler(this);
|
||||
|
||||
// Add user-session pair in authorized list
|
||||
authorizedSetter.accept(user, session);
|
||||
|
||||
// update client to acknowledge authentication
|
||||
acknowledge(true);
|
||||
}
|
||||
|
||||
private void acknowledge(boolean success) {
|
||||
try {
|
||||
session.getBasicRemote()
|
||||
.sendText(gson.toJson(Map.of("authenticated", success)));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
package ch.usi.inf.sa4.sanmarinoes.smarthut.socket;
|
||||
|
||||
import javax.websocket.server.ServerEndpointConfig;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
|
||||
import org.springframework.web.socket.server.standard.ServerEndpointRegistration;
|
||||
|
||||
/** Configures the sensor socket and maps it to the /sensor-socket path */
|
||||
@Configuration
|
||||
public class SensorSocketConfig extends ServerEndpointConfig.Configurator {
|
||||
|
||||
private SensorSocketEndpoint instance;
|
||||
|
||||
@Autowired
|
||||
public SensorSocketConfig(SensorSocketEndpoint instance) {
|
||||
this.instance = instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the sensor socket endpoint to the url /sensor-socket
|
||||
*
|
||||
* @return an endpoint registration object
|
||||
*/
|
||||
@Bean
|
||||
public ServerEndpointRegistration serverEndpointRegistration() {
|
||||
return new ServerEndpointRegistration("/sensor-socket", instance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new ServerEndpointExporter
|
||||
*
|
||||
* @return a new ServerEndpointExporter
|
||||
*/
|
||||
@Bean
|
||||
public ServerEndpointExporter endpointExporter() {
|
||||
return new ServerEndpointExporter();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T getEndpointInstance(Class<T> endpointClass) throws InstantiationException {
|
||||
try {
|
||||
@SuppressWarnings("unchecked")
|
||||
final T instance = (T) this.instance;
|
||||
return instance;
|
||||
} catch (ClassCastException e) {
|
||||
final var e2 =
|
||||
new InstantiationException("Cannot cast SensorSocketEndpoint to desired type");
|
||||
e2.initCause(e);
|
||||
throw e2;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,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<Session> unauthorizedClients = Collections.synchronizedSet(new HashSet<>());
|
||||
|
||||
private Multimap<User, Session> authorizedClients =
|
||||
Multimaps.synchronizedMultimap(HashMultimap.create());
|
||||
|
||||
@Autowired
|
||||
public SensorSocketEndpoint(AuthenticationMessageListener authenticationMessageListener) {
|
||||
this.authenticationMessageListener = authenticationMessageListener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a synchronized set of socket sessions not yet authorized with a token
|
||||
*
|
||||
* @return a synchronized set of socket sessions not yet authorized with a token
|
||||
*/
|
||||
public Set<Session> getUnauthorizedClients() {
|
||||
return unauthorizedClients;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a synchronized User to Session multimap with authorized sessions
|
||||
*
|
||||
* @return a synchronized User to Session multimap with authorized sessions
|
||||
*/
|
||||
public Multimap<User, Session> getAuthorizedClients() {
|
||||
return authorizedClients;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a message and a user, broadcasts that message in json to all associated clients and
|
||||
* returns the number of successful transfers
|
||||
*
|
||||
* @param message the message to send
|
||||
* @param u the user to which to send the message
|
||||
* @return number of successful transfer
|
||||
*/
|
||||
public long broadcast(Object message, User u) {
|
||||
final Collection<Session> sessions = authorizedClients.get(u);
|
||||
return sessions.stream()
|
||||
.parallel()
|
||||
.filter(didThrow(s -> s.getBasicRemote().sendText(gson.toJson(message))))
|
||||
.count();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}));
|
||||
}
|
||||
}
|
|
@ -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<T> {
|
||||
void apply(T input) throws Throwable;
|
||||
}
|
||||
|
||||
public static <T> List<T> toList(Iterable<T> iterable) {
|
||||
return StreamSupport.stream(iterable.spliterator(), false).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static <T> Predicate<T> didThrow(ConsumerWithException<T> consumer) {
|
||||
return (t) -> {
|
||||
try {
|
||||
consumer.apply(t);
|
||||
return true;
|
||||
} catch (Throwable e) {
|
||||
System.err.println(e.getMessage());
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,8 @@ import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.OkResponse;
|
|||
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.UserRegistrationRequest;
|
||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.error.DuplicateRegistrationException;
|
||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.error.UnauthorizedException;
|
||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.ConfirmationTokenRepository;
|
||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.UserRepository;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
import java.util.Map;
|
||||
|
@ -25,6 +27,10 @@ public class AuthenticationTests extends SmartHutTest {
|
|||
|
||||
@Autowired private TestRestTemplate restTemplate;
|
||||
|
||||
@Autowired private UserRepository userRepository;
|
||||
|
||||
@Autowired private ConfirmationTokenRepository tokenRepository;
|
||||
|
||||
private UserRegistrationRequest getDisabledUser() {
|
||||
final UserRegistrationRequest disabledUser = new UserRegistrationRequest();
|
||||
disabledUser.setName("Disabled User");
|
||||
|
@ -34,15 +40,6 @@ public class AuthenticationTests extends SmartHutTest {
|
|||
return disabledUser;
|
||||
}
|
||||
|
||||
private static final UserRegistrationRequest enabledUser = new UserRegistrationRequest();
|
||||
|
||||
static {
|
||||
enabledUser.setName("Enabled User");
|
||||
enabledUser.setEmail("enabled@example.com");
|
||||
enabledUser.setUsername("enabled");
|
||||
enabledUser.setPassword("password");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setUp() {
|
||||
final ResponseEntity<OkResponse> res =
|
||||
|
@ -50,12 +47,7 @@ public class AuthenticationTests extends SmartHutTest {
|
|||
this.url("/register"), getDisabledUser(), OkResponse.class);
|
||||
assertThat(res.getStatusCode().equals(HttpStatus.OK));
|
||||
|
||||
final ResponseEntity<OkResponse> res2 =
|
||||
this.restTemplate.postForEntity(
|
||||
this.url("/register"), enabledUser, OkResponse.class);
|
||||
assertThat(res2.getStatusCode().equals(HttpStatus.OK));
|
||||
|
||||
// TODO: email confirmation for enabledUser
|
||||
registerTestUser(restTemplate, userRepository, tokenRepository);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -230,4 +222,18 @@ public class AuthenticationTests extends SmartHutTest {
|
|||
assertThat(res.getBody() != null);
|
||||
assertThat(res.getBody().isUserDisabled());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginShouldReturnTokenWithEnabledUser() {
|
||||
final JWTRequest request = new JWTRequest();
|
||||
request.setUsernameOrEmail("enabled");
|
||||
request.setPassword("password");
|
||||
|
||||
final ResponseEntity<JWTResponse> res =
|
||||
this.restTemplate.postForEntity(url("/auth/login"), request, JWTResponse.class);
|
||||
assertThat(res.getStatusCode().equals(HttpStatus.OK));
|
||||
assertThat(res.getBody() != null);
|
||||
assertThat(res.getBody().getToken() != null);
|
||||
assertThat(!res.getBody().getToken().isEmpty());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,18 @@
|
|||
package ch.usi.inf.sa4.sanmarinoes.smarthut;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.OkResponse;
|
||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.UserRegistrationRequest;
|
||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.ConfirmationToken;
|
||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.ConfirmationTokenRepository;
|
||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.User;
|
||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.UserRepository;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.springframework.boot.test.web.client.TestRestTemplate;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
public abstract class SmartHutTest {
|
||||
private boolean setupDone = false;
|
||||
|
@ -15,6 +27,38 @@ public abstract class SmartHutTest {
|
|||
|
||||
protected void setUp() {}
|
||||
|
||||
protected static final UserRegistrationRequest enabledUser = new UserRegistrationRequest();
|
||||
|
||||
static {
|
||||
enabledUser.setName("Enabled User");
|
||||
enabledUser.setEmail("enabled@example.com");
|
||||
enabledUser.setUsername("enabled");
|
||||
enabledUser.setPassword("password");
|
||||
}
|
||||
|
||||
protected void registerTestUser(
|
||||
final TestRestTemplate restTemplate,
|
||||
final UserRepository userRepository,
|
||||
final ConfirmationTokenRepository tokenRepository) {
|
||||
final ResponseEntity<OkResponse> res2 =
|
||||
restTemplate.postForEntity(this.url("/register"), enabledUser, OkResponse.class);
|
||||
assertThat(res2.getStatusCode().equals(HttpStatus.OK));
|
||||
|
||||
final User persistedEnabledUser = userRepository.findByUsername("enabled");
|
||||
final ConfirmationToken token = tokenRepository.findByUser(persistedEnabledUser);
|
||||
|
||||
final ResponseEntity<Void> res3 =
|
||||
WebClient.create(getBaseURL())
|
||||
.get()
|
||||
.uri("/register/confirm-account?token=" + token.getConfirmationToken())
|
||||
.retrieve()
|
||||
.toBodilessEntity()
|
||||
.block();
|
||||
|
||||
assertThat(res3.getStatusCode().is2xxSuccessful());
|
||||
assertThat(userRepository.findByUsername("enabled").getEnabled());
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
void setUpHack() {
|
||||
if (!setupDone) {
|
||||
|
|
|
@ -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:
|
||||
|
|
Loading…
Reference in a new issue