From e806c188094381c7634d549999a1fc1d9450ffe8 Mon Sep 17 00:00:00 2001 From: tommi27 Date: Tue, 3 Mar 2020 11:04:58 +0100 Subject: [PATCH 01/21] added uniqueness constraints --- .../ch/usi/inf/sa4/sanmarinoes/smarthut/models/Device.java | 2 +- .../usi/inf/sa4/sanmarinoes/smarthut/models/KnobDimmer.java | 6 +++++- .../ch/usi/inf/sa4/sanmarinoes/smarthut/models/Room.java | 4 ++-- .../ch/usi/inf/sa4/sanmarinoes/smarthut/models/User.java | 6 +++--- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Device.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Device.java index 7aa65e8..e8a621b 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Device.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Device.java @@ -30,7 +30,7 @@ public abstract class Device { * The room this device belongs in, as a foreign key id. To use when updating and inserting from * a REST call. */ - @Column(name = "room_id", nullable = false) + @Column(name = "room_id", nullable = false, unique = true) @NotNull private Long roomId; diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/KnobDimmer.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/KnobDimmer.java index 0a90998..7f4056b 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/KnobDimmer.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/KnobDimmer.java @@ -33,7 +33,11 @@ public class KnobDimmer extends Dimmer { dl.setIntensity((dl.getIntensity() + 5) % 105); } else { dl.setIntensity(dl.getIntensity() + (5 - remainder)); - dl.setIntensity((dl.getIntensity() - 5) % 105); + if (dl.getIntensity() == 0) { + dl.setIntensity(100); + } else { + dl.setIntensity(dl.getIntensity() - 5); + } } } } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Room.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Room.java index e15805b..f6edfd3 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Room.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Room.java @@ -10,7 +10,7 @@ public class Room { @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; @@ -33,7 +33,7 @@ public class Room { * inserting from a REST call. */ @NotNull - @Column(name = "user_id", nullable = false) + @Column(name = "user_id", nullable = false, unique = true) private Long userId; /** The user given name of this room (e.g. 'Master bedroom') */ diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/User.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/User.java index 67f95d4..e33b130 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/User.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/User.java @@ -14,7 +14,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; @@ -24,9 +24,9 @@ public class User { @NotEmpty(message = "Please provide a full name") private String name; - /** The full name of the user */ + /** The full username of the user */ @NotNull - @Column(nullable = false) + @Column(nullable = false, unique = true) @NotEmpty(message = "Please provide a username") private String username; From 7cd2b44a447dd4c13b7d8799040c34f04f53bb76 Mon Sep 17 00:00:00 2001 From: tommi27 Date: Wed, 4 Mar 2020 15:25:53 +0100 Subject: [PATCH 02/21] added gitignore for iml files --- .gitignore | 3 +++ backend.iml | 12 ------------ 2 files changed, 3 insertions(+), 12 deletions(-) delete mode 100644 backend.iml diff --git a/.gitignore b/.gitignore index 1b1a367..ee01dda 100644 --- a/.gitignore +++ b/.gitignore @@ -136,3 +136,6 @@ gradle-app.setting # # Work around https://youtrack.jetbrains.com/issue/IDEA-116898 # gradle/wrapper/gradle-wrapper.properties + +# IntelliJ +*.iml \ No newline at end of file diff --git a/backend.iml b/backend.iml deleted file mode 100644 index 1eae0df..0000000 --- a/backend.iml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file From 749d79d51e5149fd33fd293d79f2c09ae34d5666 Mon Sep 17 00:00:00 2001 From: tommi27 Date: Wed, 4 Mar 2020 16:07:23 +0100 Subject: [PATCH 03/21] added skeleton for websockets --- build.gradle | 1 + .../smarthut/websocket/HouseEndpoint.java | 24 +++++++++++++++++ .../smarthut/websocket/WebSocketConfig.java | 26 +++++++++++++++++++ 3 files changed, 51 insertions(+) create mode 100644 src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/websocket/HouseEndpoint.java create mode 100644 src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/websocket/WebSocketConfig.java diff --git a/build.gradle b/build.gradle index d511c0b..4f4082f 100644 --- a/build.gradle +++ b/build.gradle @@ -15,6 +15,7 @@ repositories { dependencies { compile 'org.hibernate.javax.persistence:hibernate-jpa-2.1-api:1.0.0.Final' + compile "org.springframework.boot:spring-boot-starter-websocket" implementation 'org.springframework.boot:spring-boot-starter' implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-security' diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/websocket/HouseEndpoint.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/websocket/HouseEndpoint.java new file mode 100644 index 0000000..2e6de11 --- /dev/null +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/websocket/HouseEndpoint.java @@ -0,0 +1,24 @@ +package ch.usi.inf.sa4.sanmarinoes.smarthut.websocket; + +import java.io.IOException; +import javax.websocket.*; +import javax.websocket.server.ServerEndpoint; + +@ServerEndpoint( + value = + "theEndpointMustHaveAShotNameBecauseAndreaBritesMartesMaronesIsAVeryNiceProgrammerThatMustTypeThisByHand.exe") // DONE: choose path +public class HouseEndpoint { + private Session session; + + @OnOpen + public void onOpen(Session session) throws IOException {} + + @OnMessage + public void onMessage() throws IOException {} + + @OnClose + public void onClose() {} + + @OnError + public void onError() {} +} diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/websocket/WebSocketConfig.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/websocket/WebSocketConfig.java new file mode 100644 index 0000000..d5ea39a --- /dev/null +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/websocket/WebSocketConfig.java @@ -0,0 +1,26 @@ +package ch.usi.inf.sa4.sanmarinoes.smarthut.websocket; + +import org.springframework.context.annotation.Configuration; +import org.springframework.messaging.simp.config.MessageBrokerRegistry; +import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker; +import org.springframework.web.socket.config.annotation.StompEndpointRegistry; +import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer; + +@Configuration +@EnableWebSocketMessageBroker +public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { + + @Override + public void registerStompEndpoints(StompEndpointRegistry registry) { + registry.addEndpoint("/house") + .setAllowedOrigins("domainURL") + .withSockJS(); // TODO: set domain URL + } + + /** Creates message brokers in-memory to send and receive messages for topic and queue route */ + @Override + public void configureMessageBroker(MessageBrokerRegistry config) { + config.enableSimpleBroker("/topic/", "/queue/"); + config.setApplicationDestinationPrefixes("/app"); + } +} From 5995bfcad220dba7eb1222a4d4f3504580c20e50 Mon Sep 17 00:00:00 2001 From: Claudio Maggioni Date: Thu, 12 Mar 2020 17:47:40 +0100 Subject: [PATCH 04/21] Allowed failures (for now) for CPD check --- .gitlab-ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 948be0a..fc62202 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -58,3 +58,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 From 7bb05b705f721e6411690f7afd71ce0428733945 Mon Sep 17 00:00:00 2001 From: Claudio Maggioni Date: Fri, 13 Mar 2020 15:48:03 +0100 Subject: [PATCH 05/21] Neutralized id value from client in device routes for creation. Reconfigured Springfox for authentication in device and room routes. --- .../smarthut/config/SpringFoxConfig.java | 43 ++++++++++++------ .../smarthut/controller/DeviceController.java | 44 +++++++++++++++++++ .../controller/DimmableLightController.java | 1 + .../controller/KnobDimmerController.java | 1 + .../controller/MotionSensorController.java | 1 + .../controller/RegularLightController.java | 1 + .../smarthut/controller/RoomController.java | 4 +- .../smarthut/controller/SensorController.java | 1 + .../controller/SmartPlugController.java | 1 + .../smarthut/controller/SwitchController.java | 1 + .../controller/WelcomeController.java | 15 ------- .../smarthut/dto/ButtonDimmerSaveRequest.java | 4 ++ .../smarthut/dto/DeviceSaveRequest.java | 42 ++++++++++++++++++ .../dto/DimmableLightSaveRequest.java | 4 ++ .../smarthut/dto/KnobDimmerSaveRequest.java | 4 ++ .../smarthut/dto/MotionSensorSaveRequest.java | 4 ++ .../smarthut/dto/RegularLightSaveRequest.java | 4 ++ .../smarthut/dto/SensorSaveRequest.java | 4 ++ .../smarthut/dto/SmartPlugSaveRequest.java | 4 ++ .../smarthut/dto/SwitchSaveRequest.java | 4 ++ .../smarthut/error/BadDataException.java | 11 +++++ .../sa4/sanmarinoes/smarthut/models/Room.java | 14 +++--- 22 files changed, 174 insertions(+), 38 deletions(-) create mode 100644 src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/DeviceController.java delete mode 100644 src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/WelcomeController.java create mode 100644 src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/DeviceSaveRequest.java create mode 100644 src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/error/BadDataException.java diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/SpringFoxConfig.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/SpringFoxConfig.java index 4011592..2fdab4e 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/SpringFoxConfig.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/SpringFoxConfig.java @@ -1,6 +1,5 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.config; -import static springfox.documentation.builders.PathSelectors.regex; import java.util.List; import java.util.function.Predicate; @@ -10,10 +9,9 @@ import org.springframework.context.annotation.Configuration; import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.RequestHandlerSelectors; -import springfox.documentation.service.ApiInfo; -import springfox.documentation.service.ApiKey; -import springfox.documentation.service.SecurityScheme; +import springfox.documentation.service.*; import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spi.service.contexts.SecurityContext; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.swagger2.annotations.EnableSwagger2; @@ -39,7 +37,8 @@ public class SpringFoxConfig { .paths(paths()::test) .build() .apiInfo(apiInfo()) - .securitySchemes(securitySchemes()); + .securitySchemes(securitySchemes()) + .securityContexts(List.of(securityContext())); } /** @@ -51,14 +50,32 @@ public class SpringFoxConfig { return List.of(new ApiKey("Bearer", "Authorization", "header")); } - /** - * Return a Java functional API predicate for regex matches - * - * @param regex the regex to match on - * @return a Java functional API predicate - */ - private Predicate regexPredicate(final String regex) { - return regex(regex)::apply; + private SecurityContext securityContext() { + return SecurityContext.builder() + .securityReferences(defaultAuth()) + .forPaths(authenticatedPaths()::test) + .build(); + } + + private List defaultAuth() { + final AuthorizationScope authorizationScope = + new AuthorizationScope("global", "accessEverything"); + return List.of( + new SecurityReference("Bearer", new AuthorizationScope[] {authorizationScope})); + } + + private Predicate authenticatedPaths() { + return ((Predicate) PathSelectors.regex("/auth/update")::apply) + .or(PathSelectors.regex("/room.*")::apply) + .or(PathSelectors.regex("/device.*")::apply) + .or(PathSelectors.regex("/buttonDimmer.*")::apply) + .or(PathSelectors.regex("/dimmableLight.*")::apply) + .or(PathSelectors.regex("/knobDimmer.*")::apply) + .or(PathSelectors.regex("/regularLight.*")::apply) + .or(PathSelectors.regex("/sensor.*")::apply) + .or(PathSelectors.regex("/smartPlug.*")::apply) + .or(PathSelectors.regex("/switch.*")::apply) + .or(PathSelectors.regex("/motionSensor.*")::apply); } /** diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/DeviceController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/DeviceController.java new file mode 100644 index 0000000..fa3242d --- /dev/null +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/DeviceController.java @@ -0,0 +1,44 @@ +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 javax.validation.Valid; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@EnableAutoConfiguration +@RequestMapping("/device") +public class DeviceController { + + @Autowired private DeviceRepository deviceRepository; + @Autowired private RoomRepository roomRepository; + + @PutMapping + public Device update(@Valid @RequestBody DeviceSaveRequest deviceSaveRequest) + throws NotFoundException, BadDataException { + final Device d = + deviceRepository + .findById(deviceSaveRequest.getId()) + .orElseThrow(NotFoundException::new); + + // check if roomId is valid + roomRepository + .findById(deviceSaveRequest.getRoomId()) + .orElseThrow(() -> new BadDataException("roomId is not a valid room id")); + + d.setRoomId(deviceSaveRequest.getRoomId()); + d.setName(deviceSaveRequest.getName()); + + deviceRepository.save(d); + return d; + } +} diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/DimmableLightController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/DimmableLightController.java index 3220b76..063a4a6 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/DimmableLightController.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/DimmableLightController.java @@ -42,6 +42,7 @@ public class DimmableLightController { @PutMapping public DimmableLight update(@Valid @RequestBody DimmableLightSaveRequest dl) { + dl.setId(0); return this.create(dl); } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/KnobDimmerController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/KnobDimmerController.java index 81f54ea..6544c8d 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/KnobDimmerController.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/KnobDimmerController.java @@ -42,6 +42,7 @@ public class KnobDimmerController { @PutMapping public KnobDimmer update(@Valid @RequestBody KnobDimmerSaveRequest kd) { + kd.setId(0); return this.create(kd); } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/MotionSensorController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/MotionSensorController.java index a09f900..c0ba65a 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/MotionSensorController.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/MotionSensorController.java @@ -42,6 +42,7 @@ public class MotionSensorController { @PutMapping public MotionSensor update(@Valid @RequestBody MotionSensorSaveRequest ms) { + ms.setId(0); return this.create(ms); } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/RegularLightController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/RegularLightController.java index 061f12c..acffb30 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/RegularLightController.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/RegularLightController.java @@ -49,6 +49,7 @@ public class RegularLightController { @PutMapping public RegularLight update(@Valid @RequestBody RegularLightSaveRequest rl) { + rl.setId(0); return this.create(rl); } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/RoomController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/RoomController.java index 3a2ba78..a5e6436 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/RoomController.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/RoomController.java @@ -44,12 +44,12 @@ public class RoomController { newRoom.setUserId(userId); newRoom.setName(r.getName()); if (img != null) { - newRoom.setImage(img.getBytes()); + newRoom.setImage(img); } else if (setWhenNull) { newRoom.setImage(null); } if (icon != null) { - newRoom.setIcon(icon.getBytes()); + newRoom.setIcon(icon); } else if (setWhenNull) { newRoom.setIcon(null); } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SensorController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SensorController.java index d738a37..cceb05a 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SensorController.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SensorController.java @@ -43,6 +43,7 @@ public class SensorController { @PutMapping public Sensor update(@Valid @RequestBody SensorSaveRequest s) { + s.setId(0); return this.create(s); } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SmartPlugController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SmartPlugController.java index 43b66d9..e05ce72 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SmartPlugController.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SmartPlugController.java @@ -42,6 +42,7 @@ public class SmartPlugController { @PutMapping public SmartPlug update(@Valid @RequestBody SmartPlugSaveRequest sp) { + sp.setId(0); return this.create(sp); } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SwitchController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SwitchController.java index 5df72cf..9166e15 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SwitchController.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SwitchController.java @@ -42,6 +42,7 @@ public class SwitchController { @PutMapping public Switch update(@Valid @RequestBody SwitchSaveRequest s) { + s.setId(0); return this.create(s); } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/WelcomeController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/WelcomeController.java deleted file mode 100644 index a81eec6..0000000 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/WelcomeController.java +++ /dev/null @@ -1,15 +0,0 @@ -package ch.usi.inf.sa4.sanmarinoes.smarthut.controller; - -import org.springframework.boot.autoconfigure.*; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; - -@RestController -@EnableAutoConfiguration -public class WelcomeController { - - @GetMapping - ResponseEntity testConnection() { - return ResponseEntity.ok(null); - } -} diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/ButtonDimmerSaveRequest.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/ButtonDimmerSaveRequest.java index 31a22d8..7dda699 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/ButtonDimmerSaveRequest.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/ButtonDimmerSaveRequest.java @@ -60,4 +60,8 @@ public class ButtonDimmerSaveRequest { public String getName() { return name; } + + public void setId(long id) { + this.id = id; + } } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/DeviceSaveRequest.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/DeviceSaveRequest.java new file mode 100644 index 0000000..a975117 --- /dev/null +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/DeviceSaveRequest.java @@ -0,0 +1,42 @@ +package ch.usi.inf.sa4.sanmarinoes.smarthut.dto; + +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; + +public class DeviceSaveRequest { + /** Device identifier */ + private long id; + + /** + * The room this device belongs in, as a foreign key id. To use when updating and inserting from + * a REST call. + */ + @NotNull private Long roomId; + + /** The name of the device as assigned by the user (e.g. 'Master bedroom light') */ + @NotNull @NotEmpty private String name; + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public Long getRoomId() { + return roomId; + } + + public void setRoomId(Long roomId) { + this.roomId = roomId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/DimmableLightSaveRequest.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/DimmableLightSaveRequest.java index 8edff94..75fb74e 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/DimmableLightSaveRequest.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/DimmableLightSaveRequest.java @@ -66,4 +66,8 @@ public class DimmableLightSaveRequest { } this.intensity = intensity; } + + public void setId(long id) { + this.id = id; + } } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/KnobDimmerSaveRequest.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/KnobDimmerSaveRequest.java index ce053e3..73d44d2 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/KnobDimmerSaveRequest.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/KnobDimmerSaveRequest.java @@ -48,4 +48,8 @@ public class KnobDimmerSaveRequest { public Set getLights() { return lights; } + + public void setId(long id) { + this.id = id; + } } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/MotionSensorSaveRequest.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/MotionSensorSaveRequest.java index ba73495..a44c343 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/MotionSensorSaveRequest.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/MotionSensorSaveRequest.java @@ -44,4 +44,8 @@ public class MotionSensorSaveRequest { public void setDetected(boolean detected) { this.detected = detected; } + + public void setId(long id) { + this.id = id; + } } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/RegularLightSaveRequest.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/RegularLightSaveRequest.java index ac1324d..99211e5 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/RegularLightSaveRequest.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/RegularLightSaveRequest.java @@ -45,4 +45,8 @@ public class RegularLightSaveRequest { public void setOn(boolean on) { this.on = on; } + + public void setId(long id) { + this.id = id; + } } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/SensorSaveRequest.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/SensorSaveRequest.java index 421523c..0985db2 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/SensorSaveRequest.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/SensorSaveRequest.java @@ -78,4 +78,8 @@ public class SensorSaveRequest { public void setValue(int newValue) { this.value = newValue; } + + public void setId(long id) { + this.id = id; + } } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/SmartPlugSaveRequest.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/SmartPlugSaveRequest.java index 3318505..6b2f9b5 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/SmartPlugSaveRequest.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/SmartPlugSaveRequest.java @@ -45,4 +45,8 @@ public class SmartPlugSaveRequest { public void setOn(boolean on) { this.on = on; } + + public void setId(long id) { + this.id = id; + } } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/SwitchSaveRequest.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/SwitchSaveRequest.java index c7516f2..88050c0 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/SwitchSaveRequest.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/SwitchSaveRequest.java @@ -45,4 +45,8 @@ public class SwitchSaveRequest { public void setOn(boolean on) { this.on = on; } + + public void setId(long id) { + this.id = id; + } } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/error/BadDataException.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/error/BadDataException.java new file mode 100644 index 0000000..2c6c4d4 --- /dev/null +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/error/BadDataException.java @@ -0,0 +1,11 @@ +package ch.usi.inf.sa4.sanmarinoes.smarthut.error; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +@ResponseStatus(code = HttpStatus.BAD_REQUEST) +public class BadDataException extends Exception { + public BadDataException(String message) { + super(message); + } +} diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Room.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Room.java index e15805b..72859d5 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Room.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Room.java @@ -20,13 +20,11 @@ public class Room { * https://www.baeldung.com/java-base64-image-string * https://docs.oracle.com/javase/8/docs/api/java/util/Base64.html */ - @Lob - @Column(name = "icon", columnDefinition = "TEXT") - private byte[] icon; + @Column private String icon; @Lob @Column(name = "image", columnDefinition = "TEXT") - private byte[] image; + private String image; /** * User that owns the house this room is in as a foreign key id. To use when updating and @@ -65,19 +63,19 @@ public class Room { this.name = name; } - public byte[] getIcon() { + public String getIcon() { return icon; } - public void setIcon(byte[] icon) { + public void setIcon(String 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; } From 3a97d408580a9a9d0064d7d1bdefd5b738fe55e9 Mon Sep 17 00:00:00 2001 From: Claudio Maggioni Date: Fri, 13 Mar 2020 18:20:16 +0100 Subject: [PATCH 06/21] Button dimmer now with new rest conversation scheme --- .../smarthut/config/GsonConfig.java | 14 ++++ .../smarthut/config/GsonExclude.java | 10 +++ .../controller/ButtonDimmerController.java | 64 ++++++++++++--- .../controller/DimmableLightController.java | 7 -- .../InputDeviceConnectionController.java | 81 +++++++++++++++++++ .../smarthut/dto/ButtonDimmerDimRequest.java | 34 ++++++++ .../smarthut/dto/ButtonDimmerSaveRequest.java | 37 --------- .../dto/DimmableLightSaveRequest.java | 28 +------ .../smarthut/error/NotFoundException.java | 6 +- .../smarthut/models/ButtonDimmer.java | 21 +++-- .../smarthut/models/DimmableLight.java | 51 ++++++++++-- .../smarthut/models/InputConnectable.java | 18 +++++ .../smarthut/models/OutputConnectable.java | 18 +++++ .../sanmarinoes/smarthut/models/Sensor.java | 2 + 14 files changed, 293 insertions(+), 98 deletions(-) create mode 100644 src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/GsonExclude.java create mode 100644 src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/InputDeviceConnectionController.java create mode 100644 src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/ButtonDimmerDimRequest.java create mode 100644 src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/InputConnectable.java create mode 100644 src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/OutputConnectable.java diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/GsonConfig.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/GsonConfig.java index 3a2ab37..e12d9f0 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/GsonConfig.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/GsonConfig.java @@ -23,6 +23,7 @@ public class GsonConfig { private Gson gson() { final GsonBuilder builder = new GsonBuilder(); builder.registerTypeAdapter(Json.class, new SpringfoxJsonToGsonAdapter()); + builder.addSerializationExclusionStrategy(new AnnotationExclusionStrategy()); return builder.create(); } } @@ -34,3 +35,16 @@ class SpringfoxJsonToGsonAdapter implements JsonSerializer { return JsonParser.parseString(json.value()); } } + +/** GSON exclusion strategy to exclude attributes with @GsonExclude */ +class AnnotationExclusionStrategy implements ExclusionStrategy { + @Override + public boolean shouldSkipField(FieldAttributes f) { + return f.getAnnotation(GsonExclude.class) != null; + } + + @Override + public boolean shouldSkipClass(Class clazz) { + return false; + } +} diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/GsonExclude.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/GsonExclude.java new file mode 100644 index 0000000..1be5551 --- /dev/null +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/GsonExclude.java @@ -0,0 +1,10 @@ +package ch.usi.inf.sa4.sanmarinoes.smarthut.config; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface GsonExclude {} diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/ButtonDimmerController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/ButtonDimmerController.java index 3d59036..c7984ea 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/ButtonDimmerController.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/ButtonDimmerController.java @@ -2,11 +2,12 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.controller; import static ch.usi.inf.sa4.sanmarinoes.smarthut.utils.Utils.toList; +import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.ButtonDimmerDimRequest; import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.ButtonDimmerSaveRequest; 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,74 @@ import org.springframework.web.bind.annotation.*; @RestController @EnableAutoConfiguration @RequestMapping("/buttonDimmer") -public class ButtonDimmerController { - @Autowired private ButtonDimmerRepository buttonDimmerService; +public class ButtonDimmerController + extends InputDeviceConnectionController { + private ButtonDimmerRepository buttonDimmerRepository; + private DimmableLightRepository dimmableLightRepository; + + @Autowired + protected ButtonDimmerController( + ButtonDimmerRepository inputRepository, DimmableLightRepository outputRepository) { + super(inputRepository, outputRepository); + this.buttonDimmerRepository = inputRepository; + this.dimmableLightRepository = outputRepository; + } @GetMapping public List findAll() { - return toList(buttonDimmerService.findAll()); + return toList(buttonDimmerRepository.findAll()); } @GetMapping("/{id}") public ButtonDimmer findById(@PathVariable("id") long id) throws NotFoundException { - return buttonDimmerService.findById(id).orElseThrow(NotFoundException::new); + return buttonDimmerRepository.findById(id).orElseThrow(NotFoundException::new); } @PostMapping public ButtonDimmer create(@Valid @RequestBody final ButtonDimmerSaveRequest bd) { ButtonDimmer newBD = new ButtonDimmer(); - newBD.setLights(bd.getLights()); - newBD.setId(bd.getId()); newBD.setName(bd.getName()); newBD.setRoomId(bd.getRoomId()); - return buttonDimmerService.save(newBD); + return buttonDimmerRepository.save(newBD); } - @PutMapping - public ButtonDimmer update(@Valid @RequestBody ButtonDimmerSaveRequest bd) { - return this.create(bd); + @PutMapping("/dim") + public Set dim(@Valid @RequestBody final ButtonDimmerDimRequest bd) + throws NotFoundException { + final ButtonDimmer buttonDimmer = + buttonDimmerRepository.findById(bd.getId()).orElseThrow(NotFoundException::new); + + switch (bd.getDimType()) { + case UP: + buttonDimmer.increaseIntensity(); + break; + case DOWN: + buttonDimmer.decreaseIntensity(); + break; + } + + dimmableLightRepository.saveAll(buttonDimmer.getLights()); + + return buttonDimmer.getOutputs(); + } + + @PostMapping("/{id}/lights") + public Set addLight( + @PathVariable("id") long inputId, @RequestParam("lightId") Long lightId) + throws NotFoundException { + return addOutput(inputId, lightId); + } + + @DeleteMapping("/{id}/lights") + public Set removeLight( + @PathVariable("id") long inputId, @RequestParam("lightId") Long lightId) + throws NotFoundException { + return removeOutput(inputId, lightId); } @DeleteMapping("/{id}") public void delete(@PathVariable("id") long id) { - buttonDimmerService.deleteById(id); + buttonDimmerRepository.deleteById(id); } } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/DimmableLightController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/DimmableLightController.java index 063a4a6..898b035 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/DimmableLightController.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/DimmableLightController.java @@ -33,19 +33,12 @@ public class DimmableLightController { 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); } - @PutMapping - public DimmableLight update(@Valid @RequestBody DimmableLightSaveRequest dl) { - dl.setId(0); - return this.create(dl); - } - @DeleteMapping("/{id}") public void delete(@PathVariable("id") long id) { dimmableLightService.deleteById(id); diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/InputDeviceConnectionController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/InputDeviceConnectionController.java new file mode 100644 index 0000000..37e17e1 --- /dev/null +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/InputDeviceConnectionController.java @@ -0,0 +1,81 @@ +package ch.usi.inf.sa4.sanmarinoes.smarthut.controller; + +import ch.usi.inf.sa4.sanmarinoes.smarthut.error.NotFoundException; +import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*; +import java.util.Set; + +/** + * An abstract controller for an input device that has output connected to it. Aids to create the + * output add and output remove route + * + * @param the type of device this controller is for + * @param the output device attached to I + */ +public abstract class InputDeviceConnectionController< + I extends InputDevice & OutputConnectable, + O extends OutputDevice & InputConnectable> { + + private class IOPair { + private final I input; + private final O output; + + private IOPair(I input, O output) { + this.input = input; + this.output = output; + } + } + + private DeviceRepository inputRepository; + + private DeviceRepository outputReposiory; + + protected InputDeviceConnectionController( + DeviceRepository inputRepository, DeviceRepository outputRepository) { + this.inputRepository = inputRepository; + this.outputReposiory = outputRepository; + } + + 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 addOutput(Long inputId, Long outputId) throws NotFoundException { + final IOPair pair = checkConnectionIDs(inputId, outputId); + pair.input.getOutputs().add(pair.output); + pair.output.connect(pair.input.getId()); + 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 removeOutput(Long inputId, Long outputId) throws NotFoundException { + final IOPair pair = checkConnectionIDs(inputId, outputId); + pair.input.getOutputs().remove(pair.output); + pair.output.connect(null); + outputReposiory.save(pair.output); + return pair.input.getOutputs(); + } +} diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/ButtonDimmerDimRequest.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/ButtonDimmerDimRequest.java new file mode 100644 index 0000000..8e07015 --- /dev/null +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/ButtonDimmerDimRequest.java @@ -0,0 +1,34 @@ +package ch.usi.inf.sa4.sanmarinoes.smarthut.dto; + +import javax.validation.constraints.NotNull; + +/** A 'dim' event from a button dimmer. */ +public class ButtonDimmerDimRequest { + + /** The device id */ + @NotNull private Long id; + + public enum DimType { + UP, + DOWN; + } + + /** Whether the dim is up or down */ + @NotNull private DimType dimType; + + public DimType getDimType() { + return dimType; + } + + public void setDimType(DimType dimType) { + this.dimType = dimType; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } +} diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/ButtonDimmerSaveRequest.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/ButtonDimmerSaveRequest.java index 7dda699..bb0b0df 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/ButtonDimmerSaveRequest.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/ButtonDimmerSaveRequest.java @@ -1,21 +1,8 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.dto; -import ch.usi.inf.sa4.sanmarinoes.smarthut.models.DimmableLight; -import ch.usi.inf.sa4.sanmarinoes.smarthut.models.Room; -import java.util.HashSet; -import java.util.Set; -import javax.persistence.*; import javax.validation.constraints.NotNull; public class ButtonDimmerSaveRequest { - @Lob private Set lights = new HashSet(); - - /** Device identifier */ - private long id; - - /** The room this device belongs in */ - private Room room; - /** * The room this device belongs in, as a foreign key id. To use when updating and inserting from * a REST call. @@ -25,18 +12,6 @@ public class ButtonDimmerSaveRequest { /** The name of the device as assigned by the user (e.g. 'Master bedroom light') */ @NotNull private String name; - public Set getLights() { - return this.lights; - } - - public void setLights(Set newLights) { - this.lights = newLights; - } - - public void setRoom(Room room) { - this.room = room; - } - public void setRoomId(Long roomId) { this.roomId = roomId; } @@ -45,14 +20,6 @@ public class ButtonDimmerSaveRequest { this.name = name; } - public long getId() { - return id; - } - - public Room getRoom() { - return room; - } - public Long getRoomId() { return roomId; } @@ -60,8 +27,4 @@ public class ButtonDimmerSaveRequest { public String getName() { return name; } - - public void setId(long id) { - this.id = id; - } } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/DimmableLightSaveRequest.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/DimmableLightSaveRequest.java index 75fb74e..03e3eb0 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/DimmableLightSaveRequest.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/DimmableLightSaveRequest.java @@ -1,6 +1,5 @@ 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; @@ -13,12 +12,6 @@ public class DimmableLightSaveRequest { @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 +21,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 +29,6 @@ public class DimmableLightSaveRequest { this.name = name; } - public long getId() { - return id; - } - - public Room getRoom() { - return room; - } - public Long getRoomId() { return roomId; } @@ -60,14 +41,7 @@ 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 void setId(long id) { - this.id = id; - } } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/error/NotFoundException.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/error/NotFoundException.java index 1d5f90d..471107f 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/error/NotFoundException.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/error/NotFoundException.java @@ -6,6 +6,10 @@ import org.springframework.web.bind.annotation.ResponseStatus; @ResponseStatus(code = HttpStatus.NOT_FOUND) public class NotFoundException extends Exception { public NotFoundException() { - super("Not Found"); + super("Not found"); + } + + public NotFoundException(String what) { + super(what + " not found"); } } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/ButtonDimmer.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/ButtonDimmer.java index 1e5f702..2e96a73 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/ButtonDimmer.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/ButtonDimmer.java @@ -1,6 +1,5 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.models; - import java.util.Set; import javax.persistence.Entity; import javax.persistence.OneToMany; @@ -10,7 +9,10 @@ import javax.persistence.OneToMany; * dimmer with a '+' and a '-' button) */ @Entity -public class ButtonDimmer extends Dimmer { +public class ButtonDimmer extends Dimmer implements OutputConnectable { + /** The delta amount to apply to a increase or decrease intensity */ + public static final int DIM_INCREMENT = 10; + public ButtonDimmer() { super("button-dimmer"); } @@ -18,17 +20,19 @@ public class ButtonDimmer extends Dimmer { @OneToMany(mappedBy = "dimmer") private Set lights; - /** Increases the current intensity level of the dimmable light by 1 */ + /** Increases the current intensity level of the dimmable light by DIM_INCREMENT */ public void increaseIntensity() { for (DimmableLight dl : lights) { - dl.setIntensity(dl.getIntensity() + 1); + dl.setIntensity(dl.getIntensity() + DIM_INCREMENT); + System.out.println("malusa: " + dl.getIntensity()); } } - /** 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); + dl.setIntensity(dl.getIntensity() - DIM_INCREMENT); + System.out.println("malusa: " + dl.getIntensity()); } } @@ -64,6 +68,11 @@ public class ButtonDimmer extends Dimmer { return this.lights; } + @Override + public Set getOutputs() { + return this.lights; + } + public void setLights(Set newLights) { this.lights = newLights; } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/DimmableLight.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/DimmableLight.java index a71afbc..9f82dc9 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/DimmableLight.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/DimmableLight.java @@ -1,7 +1,9 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.models; +import ch.usi.inf.sa4.sanmarinoes.smarthut.config.GsonExclude; import javax.persistence.Column; import javax.persistence.Entity; +import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.validation.constraints.Max; import javax.validation.constraints.Min; @@ -9,18 +11,24 @@ import javax.validation.constraints.NotNull; /** Represent a dimmable light */ @Entity -public class DimmableLight extends Light { +public class DimmableLight extends Light implements InputConnectable { 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 +36,39 @@ 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; + setOn(false); + } else { + setOn(true); + if (intensity > 100) { + this.intensity = 100; + } else { + this.intensity = intensity; + } } - this.intensity = intensity; + } + + @Override + public void setOn(boolean on) { + super.setOn(on); + if (on) { + intensity = 100; + } else { + intensity = 0; + } + } + + @Override + public void connect(Long inputId) { + this.dimmerId = inputId; } } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/InputConnectable.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/InputConnectable.java new file mode 100644 index 0000000..74b6487 --- /dev/null +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/InputConnectable.java @@ -0,0 +1,18 @@ +package ch.usi.inf.sa4.sanmarinoes.smarthut.models; + + +/** + * An output device to which an input can be connected + * + * @param the type of input device that can be connected to this device + */ +public interface InputConnectable { + + /** + * Connect an input device to this output device. This method should set the corresponding FK in + * the entity. + * + * @param inputId the input device id, null for deleting the connection + */ + void connect(Long inputId); +} diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/OutputConnectable.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/OutputConnectable.java new file mode 100644 index 0000000..c6360e9 --- /dev/null +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/OutputConnectable.java @@ -0,0 +1,18 @@ +package ch.usi.inf.sa4.sanmarinoes.smarthut.models; + +import java.util.Set; + +/** + * An input device to which outputs can be connected + * + * @param the type of output device that can be connected to this device + */ +public interface OutputConnectable { + + /** + * Get the set of all output devices connected to this device + * + * @return The set of outputs connected to this device + */ + Set getOutputs(); +} diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Sensor.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Sensor.java index e352a52..b1d98a2 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Sensor.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Sensor.java @@ -42,6 +42,8 @@ public class Sensor extends InputDevice { public void setSensor(SensorType sensor) { this.sensor = sensor; + + // TODO: setup hook for sockets live update } public int getValue() { From 6fef7663dd6fb016de93200f6fec2e0ef1a310b9 Mon Sep 17 00:00:00 2001 From: Claudio Maggioni Date: Fri, 13 Mar 2020 18:48:19 +0100 Subject: [PATCH 07/21] Knob dimmer now with new rest conversation scheme --- .../InputDeviceConnectionController.java | 2 +- .../controller/KnobDimmerController.java | 57 ++++++++++++++----- .../smarthut/dto/KnobDimmerDimRequest.java | 33 +++++++++++ .../smarthut/dto/KnobDimmerSaveRequest.java | 25 -------- .../smarthut/models/ButtonDimmer.java | 2 - .../smarthut/models/DimmableLight.java | 2 +- .../smarthut/models/KnobDimmer.java | 47 ++++----------- 7 files changed, 88 insertions(+), 80 deletions(-) create mode 100644 src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/KnobDimmerDimRequest.java diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/InputDeviceConnectionController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/InputDeviceConnectionController.java index 37e17e1..cbcdc98 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/InputDeviceConnectionController.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/InputDeviceConnectionController.java @@ -13,7 +13,7 @@ import java.util.Set; */ public abstract class InputDeviceConnectionController< I extends InputDevice & OutputConnectable, - O extends OutputDevice & InputConnectable> { + O extends OutputDevice & InputConnectable> { private class IOPair { private final I input; diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/KnobDimmerController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/KnobDimmerController.java index 6544c8d..82d9507 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/KnobDimmerController.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/KnobDimmerController.java @@ -2,11 +2,12 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.controller; import static ch.usi.inf.sa4.sanmarinoes.smarthut.utils.Utils.toList; +import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.KnobDimmerDimRequest; import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.KnobDimmerSaveRequest; 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,39 +16,67 @@ import org.springframework.web.bind.annotation.*; @RestController @EnableAutoConfiguration @RequestMapping("/knobDimmer") -public class KnobDimmerController { +public class KnobDimmerController + extends InputDeviceConnectionController { - @Autowired private KnobDimmerRepository knobDimmerService; + @Autowired private KnobDimmerRepository knobDimmerRepository; + @Autowired private DimmableLightRepository dimmableLightRepository; + + @Autowired + protected KnobDimmerController( + KnobDimmerRepository inputRepository, DimmableLightRepository outputRepository) { + super(inputRepository, outputRepository); + this.knobDimmerRepository = inputRepository; + this.dimmableLightRepository = outputRepository; + } @GetMapping public List findAll() { - return toList(knobDimmerService.findAll()); + return toList(knobDimmerRepository.findAll()); } @GetMapping("/{id}") public KnobDimmer findById(@PathVariable("id") long id) throws NotFoundException { - return knobDimmerService.findById(id).orElseThrow(NotFoundException::new); + return knobDimmerRepository.findById(id).orElseThrow(NotFoundException::new); } @PostMapping public KnobDimmer create(@Valid @RequestBody KnobDimmerSaveRequest kd) { 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) { - kd.setId(0); - return this.create(kd); + @PutMapping("/dimTo") + public Set dimTo(@Valid @RequestBody final KnobDimmerDimRequest bd) + throws NotFoundException { + final KnobDimmer dimmer = + knobDimmerRepository.findById(bd.getId()).orElseThrow(NotFoundException::new); + + dimmer.setLightIntensity(bd.getIntensity()); + dimmableLightRepository.saveAll(dimmer.getLights()); + + return dimmer.getOutputs(); + } + + @PostMapping("/{id}/lights") + public Set addLight( + @PathVariable("id") long inputId, @RequestParam("lightId") Long lightId) + throws NotFoundException { + return addOutput(inputId, lightId); + } + + @DeleteMapping("/{id}/lights") + public Set removeLight( + @PathVariable("id") long inputId, @RequestParam("lightId") Long lightId) + throws NotFoundException { + return removeOutput(inputId, lightId); } @DeleteMapping("/{id}") public void delete(@PathVariable("id") long id) { - knobDimmerService.deleteById(id); + knobDimmerRepository.deleteById(id); } } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/KnobDimmerDimRequest.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/KnobDimmerDimRequest.java new file mode 100644 index 0000000..6df303a --- /dev/null +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/KnobDimmerDimRequest.java @@ -0,0 +1,33 @@ +package ch.usi.inf.sa4.sanmarinoes.smarthut.dto; + +import javax.validation.constraints.Max; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; + +public class KnobDimmerDimRequest { + + /** The device id */ + @NotNull private Long id; + + /** The absolute intensity value */ + @NotNull + @Min(0) + @Max(100) + private Integer intensity; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Integer getIntensity() { + return intensity; + } + + public void setIntensity(Integer intensity) { + this.intensity = intensity; + } +} diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/KnobDimmerSaveRequest.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/KnobDimmerSaveRequest.java index 73d44d2..b1c0075 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/KnobDimmerSaveRequest.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/KnobDimmerSaveRequest.java @@ -1,17 +1,8 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.dto; -import ch.usi.inf.sa4.sanmarinoes.smarthut.models.DimmableLight; -import java.util.HashSet; -import java.util.Set; -import javax.persistence.Lob; import javax.validation.constraints.NotNull; public class KnobDimmerSaveRequest { - @Lob private Set lights = new HashSet(); - - /** Device identifier */ - private long id; - /** * The room this device belongs in, as a foreign key id. To use when updating and inserting from * a REST call. @@ -29,10 +20,6 @@ public class KnobDimmerSaveRequest { this.name = name; } - public long getId() { - return id; - } - public Long getRoomId() { return roomId; } @@ -40,16 +27,4 @@ public class KnobDimmerSaveRequest { public String getName() { return name; } - - public void setLights(Set lights) { - this.lights = lights; - } - - public Set getLights() { - return lights; - } - - public void setId(long id) { - this.id = id; - } } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/ButtonDimmer.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/ButtonDimmer.java index 2e96a73..17c09f5 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/ButtonDimmer.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/ButtonDimmer.java @@ -24,7 +24,6 @@ public class ButtonDimmer extends Dimmer implements OutputConnectable { +public class DimmableLight extends Light implements InputConnectable { public DimmableLight() { super("light"); diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/KnobDimmer.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/KnobDimmer.java index 0a90998..35d243c 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/KnobDimmer.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/KnobDimmer.java @@ -9,7 +9,7 @@ import javax.persistence.OneToMany; * value, like a knob) */ @Entity -public class KnobDimmer extends Dimmer { +public class KnobDimmer extends Dimmer implements OutputConnectable { public KnobDimmer() { super("knob-dimmer"); } @@ -18,49 +18,17 @@ public class KnobDimmer extends Dimmer { private Set lights; /** - * Increases or decreases the current intensity level by 5, moving between absolute multiples of - * 5 between 0 and 100, of all dimmable lights mapped to this knob + * Sets absolutely the intensity level of all lights connected * - * @param inc The direction the knob is turned with + * @param intensity the intensity (must be from 0 to 100) */ - public void modifyIntensity(boolean inc) { + public void setLightIntensity(int intensity) { 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); - } + dl.setIntensity(intensity); } } - /** - * Adds a DimmableLight to this set of DimmableLights - * - * @param dl The DimmableLight to be added - */ - public void addLight(DimmableLight dl) { - lights.add(dl); - } - - /** - * Removes the given DimmableLight - * - * @param dl The DimmableLight to be removed - */ - public void removeLight(DimmableLight dl) { - lights.remove(dl); - } - - /** Clears this set */ - public void clearSet() { - lights.clear(); - } - public void setLights(Set lights) { this.lights = lights; } @@ -68,4 +36,9 @@ public class KnobDimmer extends Dimmer { public Set getLights() { return lights; } + + @Override + public Set getOutputs() { + return lights; + } } From ed2900a3bc4d5e54d666252bcf7c08add800089f Mon Sep 17 00:00:00 2001 From: Claudio Maggioni Date: Sat, 14 Mar 2020 12:40:28 +0100 Subject: [PATCH 08/21] New abstraction for connections --- .../controller/ButtonDimmerController.java | 8 +-- .../InputDeviceConnectionController.java | 29 +++++++--- .../controller/KnobDimmerController.java | 8 +-- .../smarthut/models/ButtonDimmer.java | 58 +++---------------- .../smarthut/models/Connector.java | 47 +++++++++++++++ .../smarthut/models/DimmableLight.java | 31 +++++----- .../sanmarinoes/smarthut/models/Dimmer.java | 15 +++++ .../smarthut/models/InputConnectable.java | 18 ------ .../smarthut/models/InputDevice.java | 5 ++ .../smarthut/models/KnobDimmer.java | 27 ++------- .../sanmarinoes/smarthut/models/Light.java | 31 ---------- .../smarthut/models/OutputConnectable.java | 18 ------ .../smarthut/models/OutputDevice.java | 4 +- .../smarthut/models/RegularLight.java | 21 ++++++- .../smarthut/models/SmartPlug.java | 4 +- .../smarthut/models/Switchable.java | 24 ++++++++ 16 files changed, 170 insertions(+), 178 deletions(-) create mode 100644 src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Connector.java delete mode 100644 src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/InputConnectable.java delete mode 100644 src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Light.java delete mode 100644 src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/OutputConnectable.java create mode 100644 src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Switchable.java diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/ButtonDimmerController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/ButtonDimmerController.java index c7984ea..92d14db 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/ButtonDimmerController.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/ButtonDimmerController.java @@ -24,7 +24,7 @@ public class ButtonDimmerController @Autowired protected ButtonDimmerController( ButtonDimmerRepository inputRepository, DimmableLightRepository outputRepository) { - super(inputRepository, outputRepository); + super(inputRepository, outputRepository, ButtonDimmer.CONNECTOR); this.buttonDimmerRepository = inputRepository; this.dimmableLightRepository = outputRepository; } @@ -63,20 +63,20 @@ public class ButtonDimmerController break; } - dimmableLightRepository.saveAll(buttonDimmer.getLights()); + dimmableLightRepository.saveAll(buttonDimmer.getOutputs()); return buttonDimmer.getOutputs(); } @PostMapping("/{id}/lights") - public Set addLight( + public Set addLight( @PathVariable("id") long inputId, @RequestParam("lightId") Long lightId) throws NotFoundException { return addOutput(inputId, lightId); } @DeleteMapping("/{id}/lights") - public Set removeLight( + public Set removeLight( @PathVariable("id") long inputId, @RequestParam("lightId") Long lightId) throws NotFoundException { return removeOutput(inputId, lightId); diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/InputDeviceConnectionController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/InputDeviceConnectionController.java index cbcdc98..52f3483 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/InputDeviceConnectionController.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/InputDeviceConnectionController.java @@ -12,8 +12,7 @@ import java.util.Set; * @param the output device attached to I */ public abstract class InputDeviceConnectionController< - I extends InputDevice & OutputConnectable, - O extends OutputDevice & InputConnectable> { + I extends InputDevice, O extends OutputDevice> { private class IOPair { private final I input; @@ -29,10 +28,22 @@ public abstract class InputDeviceConnectionController< private DeviceRepository outputReposiory; + private Connector connector; + + /** + * Contstructs the controller by requiring essential object for the controller implementation + * + * @param inputRepository the input device repository + * @param outputRepository the output device repository + * @param connector a appropriate Connector instance for the I and O tyoes. + */ protected InputDeviceConnectionController( - DeviceRepository inputRepository, DeviceRepository outputRepository) { + DeviceRepository inputRepository, + DeviceRepository outputRepository, + Connector connector) { this.inputRepository = inputRepository; this.outputReposiory = outputRepository; + this.connector = connector; } private IOPair checkConnectionIDs(Long inputId, Long outputId) throws NotFoundException { @@ -55,10 +66,10 @@ public abstract class InputDeviceConnectionController< * @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 addOutput(Long inputId, Long outputId) throws NotFoundException { + protected Set addOutput(Long inputId, Long outputId) + throws NotFoundException { final IOPair pair = checkConnectionIDs(inputId, outputId); - pair.input.getOutputs().add(pair.output); - pair.output.connect(pair.input.getId()); + connector.connect(pair.input, pair.output, true); outputReposiory.save(pair.output); return pair.input.getOutputs(); } @@ -71,10 +82,10 @@ public abstract class InputDeviceConnectionController< * @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 removeOutput(Long inputId, Long outputId) throws NotFoundException { + protected Set removeOutput(Long inputId, Long outputId) + throws NotFoundException { final IOPair pair = checkConnectionIDs(inputId, outputId); - pair.input.getOutputs().remove(pair.output); - pair.output.connect(null); + connector.connect(pair.input, pair.output, false); outputReposiory.save(pair.output); return pair.input.getOutputs(); } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/KnobDimmerController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/KnobDimmerController.java index 82d9507..4d51733 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/KnobDimmerController.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/KnobDimmerController.java @@ -25,7 +25,7 @@ public class KnobDimmerController @Autowired protected KnobDimmerController( KnobDimmerRepository inputRepository, DimmableLightRepository outputRepository) { - super(inputRepository, outputRepository); + super(inputRepository, outputRepository, KnobDimmer.CONNECTOR); this.knobDimmerRepository = inputRepository; this.dimmableLightRepository = outputRepository; } @@ -56,20 +56,20 @@ public class KnobDimmerController knobDimmerRepository.findById(bd.getId()).orElseThrow(NotFoundException::new); dimmer.setLightIntensity(bd.getIntensity()); - dimmableLightRepository.saveAll(dimmer.getLights()); + dimmableLightRepository.saveAll(dimmer.getOutputs()); return dimmer.getOutputs(); } @PostMapping("/{id}/lights") - public Set addLight( + public Set addLight( @PathVariable("id") long inputId, @RequestParam("lightId") Long lightId) throws NotFoundException { return addOutput(inputId, lightId); } @DeleteMapping("/{id}/lights") - public Set removeLight( + public Set removeLight( @PathVariable("id") long inputId, @RequestParam("lightId") Long lightId) throws NotFoundException { return removeOutput(inputId, lightId); diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/ButtonDimmer.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/ButtonDimmer.java index 17c09f5..85d4fe0 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/ButtonDimmer.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/ButtonDimmer.java @@ -1,77 +1,35 @@ 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 * dimmer with a '+' and a '-' button) */ @Entity -public class ButtonDimmer extends Dimmer implements OutputConnectable { +public class ButtonDimmer extends Dimmer { + + public static final Connector CONNECTOR = + Connector.basic(ButtonDimmer::getOutputs, DimmableLight::setDimmerId); + /** The delta amount to apply to a increase or decrease intensity */ - public static final int DIM_INCREMENT = 10; + private static final int DIM_INCREMENT = 10; public ButtonDimmer() { super("button-dimmer"); } - @OneToMany(mappedBy = "dimmer") - private Set lights; - /** Increases the current intensity level of the dimmable light by DIM_INCREMENT */ public void increaseIntensity() { - for (DimmableLight dl : lights) { + for (DimmableLight dl : getOutputs()) { dl.setIntensity(dl.getIntensity() + DIM_INCREMENT); } } /** Decreases the current intensity level of the dimmable light by DIM_INCREMENT */ public void decreaseIntensity() { - for (DimmableLight dl : lights) { + for (DimmableLight dl : getOutputs()) { dl.setIntensity(dl.getIntensity() - DIM_INCREMENT); } } - - /** - * Adds a DimmableLight to this set of DimmableLights - * - * @param dl The DimmableLight to be added - */ - public void addLight(DimmableLight dl) { - lights.add(dl); - } - - /** - * Removes the given DimmableLight - * - * @param dl The DimmableLight to be removed - */ - public void removeLight(DimmableLight dl) { - lights.remove(dl); - } - - /** Clears this set */ - public void clearSet() { - lights.clear(); - } - - /** - * Get the lights - * - * @return duh - */ - public Set getLights() { - return this.lights; - } - - @Override - public Set getOutputs() { - return this.lights; - } - - public void setLights(Set newLights) { - this.lights = newLights; - } } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Connector.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Connector.java new file mode 100644 index 0000000..dd68b10 --- /dev/null +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Connector.java @@ -0,0 +1,47 @@ +package ch.usi.inf.sa4.sanmarinoes.smarthut.models; + +import java.util.Set; +import java.util.function.BiConsumer; +import java.util.function.Function; + +/** + * A rule on how to connect an input device type to an output device type + * + * @param the input device type + * @param the output device type + */ +@FunctionalInterface +public interface Connector { + + /** + * Connects or disconnects input to output + * + * @param input the input device + * @param output the output device + * @param connect true if connection, false if disconnection + */ + void connect(I input, O output, boolean connect); + + /** + * Produces a basic implementation of a connector, assuming there is a OneToMany relationship + * between J and K + * + * @param outputsGetter the getter method of the set of outputs on the input class + * @param inputSetter the setter method for the input id on the output class + * @param the input device type + * @param the output device type + * @return a Connector implementation for the pair of types J and K + */ + static Connector basic( + Function> outputsGetter, BiConsumer inputSetter) { + return (i, o, connect) -> { + if (connect) { + outputsGetter.apply(i).add(o); + } else { + outputsGetter.apply(i).remove(o); + } + + inputSetter.accept(o, connect ? i.getId() : null); + }; + } +} diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/DimmableLight.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/DimmableLight.java index 486203b..277a7e9 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/DimmableLight.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/DimmableLight.java @@ -11,7 +11,7 @@ import javax.validation.constraints.NotNull; /** Represent a dimmable light */ @Entity -public class DimmableLight extends Light implements InputConnectable { +public class DimmableLight extends OutputDevice implements Switchable { public DimmableLight() { super("light"); @@ -46,29 +46,24 @@ public class DimmableLight extends Light implements InputConnectable { public void setIntensity(Integer intensity) { if (intensity <= 0) { this.intensity = 0; - setOn(false); + } else if (intensity > 100) { + this.intensity = 100; } else { - setOn(true); - if (intensity > 100) { - this.intensity = 100; - } else { - this.intensity = intensity; - } + this.intensity = intensity; } } + @Override + public boolean isOn() { + return intensity != 0; + } + @Override public void setOn(boolean on) { - super.setOn(on); - if (on) { - intensity = 100; - } else { - intensity = 0; - } + intensity = on ? 100 : 0; } - @Override - public void connect(Long inputId) { - this.dimmerId = inputId; - } + public void setDimmerId(Long dimmerId) { + this.dimmerId = dimmerId; + }; } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Dimmer.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Dimmer.java index 2658b35..af00025 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Dimmer.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Dimmer.java @@ -1,8 +1,10 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.models; +import java.util.Set; import javax.persistence.Entity; import javax.persistence.Inheritance; import javax.persistence.InheritanceType; +import javax.persistence.OneToMany; /** Represents a generic dimmer input device */ @Entity @@ -11,4 +13,17 @@ public abstract class Dimmer extends InputDevice { public Dimmer(String kind) { super(kind); } + + @OneToMany(mappedBy = "dimmer") + private Set lights; + + /** + * Get the lights connected to this dimmer + * + * @return duh + */ + @Override + public Set getOutputs() { + return this.lights; + } } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/InputConnectable.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/InputConnectable.java deleted file mode 100644 index 74b6487..0000000 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/InputConnectable.java +++ /dev/null @@ -1,18 +0,0 @@ -package ch.usi.inf.sa4.sanmarinoes.smarthut.models; - - -/** - * An output device to which an input can be connected - * - * @param the type of input device that can be connected to this device - */ -public interface InputConnectable { - - /** - * Connect an input device to this output device. This method should set the corresponding FK in - * the entity. - * - * @param inputId the input device id, null for deleting the connection - */ - void connect(Long inputId); -} diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/InputDevice.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/InputDevice.java index e632178..ad96347 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/InputDevice.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/InputDevice.java @@ -1,5 +1,6 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.models; +import java.util.Set; import javax.persistence.Entity; /** @@ -11,4 +12,8 @@ public abstract class InputDevice extends Device { public InputDevice(String kind) { super(kind, FlowType.INPUT); } + + public Set getOutputs() { + return Set.of(); + } } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/KnobDimmer.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/KnobDimmer.java index 35d243c..d4eea0e 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/KnobDimmer.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/KnobDimmer.java @@ -1,44 +1,29 @@ 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 * value, like a knob) */ @Entity -public class KnobDimmer extends Dimmer implements OutputConnectable { +public class KnobDimmer extends Dimmer { + + public static final Connector CONNECTOR = + Connector.basic(KnobDimmer::getOutputs, DimmableLight::setDimmerId); + public KnobDimmer() { super("knob-dimmer"); } - @OneToMany(mappedBy = "dimmer") - private Set lights; - /** * Sets absolutely the intensity level of all lights connected * * @param intensity the intensity (must be from 0 to 100) */ public void setLightIntensity(int intensity) { - - for (DimmableLight dl : lights) { + for (DimmableLight dl : getOutputs()) { dl.setIntensity(intensity); } } - - public void setLights(Set lights) { - this.lights = lights; - } - - public Set getLights() { - return lights; - } - - @Override - public Set getOutputs() { - return lights; - } } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Light.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Light.java deleted file mode 100644 index 68a819b..0000000 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Light.java +++ /dev/null @@ -1,31 +0,0 @@ -package ch.usi.inf.sa4.sanmarinoes.smarthut.models; - -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.Inheritance; -import javax.persistence.InheritanceType; -import javax.validation.constraints.NotNull; - -/** Represents a generic light */ -@Entity -@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) -public abstract class Light extends OutputDevice { - - /** Whether the light is on or not */ - @Column(name = "light_on", nullable = false) - @NotNull - boolean on; - - protected Light(String kind) { - super(kind); - this.on = false; - } - - public boolean isOn() { - return on; - } - - public void setOn(boolean on) { - this.on = on; - } -} diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/OutputConnectable.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/OutputConnectable.java deleted file mode 100644 index c6360e9..0000000 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/OutputConnectable.java +++ /dev/null @@ -1,18 +0,0 @@ -package ch.usi.inf.sa4.sanmarinoes.smarthut.models; - -import java.util.Set; - -/** - * An input device to which outputs can be connected - * - * @param the type of output device that can be connected to this device - */ -public interface OutputConnectable { - - /** - * Get the set of all output devices connected to this device - * - * @return The set of outputs connected to this device - */ - Set getOutputs(); -} diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/OutputDevice.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/OutputDevice.java index 39e5dd0..c5b401f 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/OutputDevice.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/OutputDevice.java @@ -1,8 +1,6 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.models; -import javax.persistence.Entity; -import javax.persistence.Inheritance; -import javax.persistence.InheritanceType; +import javax.persistence.*; /** * Represents a generic output device, i.e. something that causes some behaviour (light, smartPlugs, diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/RegularLight.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/RegularLight.java index 8f07377..02b5f35 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/RegularLight.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/RegularLight.java @@ -1,11 +1,30 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.models; +import javax.persistence.Column; import javax.persistence.Entity; +import javax.validation.constraints.NotNull; /** Represents a standard non-dimmable light */ @Entity -public class RegularLight extends Light { +public class RegularLight extends OutputDevice implements Switchable { + + /** Whether the light is on or not */ + @Column(name = "light_on", nullable = false) + @NotNull + boolean on; + public RegularLight() { super("regular-light"); + this.on = false; + } + + @Override + public boolean isOn() { + return on; + } + + @Override + public void setOn(boolean on) { + this.on = on; } } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/SmartPlug.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/SmartPlug.java index e0d7981..6a77a13 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/SmartPlug.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/SmartPlug.java @@ -6,17 +6,19 @@ import javax.validation.constraints.NotNull; /** A smart plug that can be turned either on or off */ @Entity -public class SmartPlug extends OutputDevice { +public class SmartPlug extends OutputDevice implements Switchable { /** Whether the smart plug is on */ @Column(name = "smart_plug_on", nullable = false) @NotNull private boolean on; + @Override public boolean isOn() { return on; } + @Override public void setOn(boolean on) { this.on = on; } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Switchable.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Switchable.java new file mode 100644 index 0000000..e13eea1 --- /dev/null +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Switchable.java @@ -0,0 +1,24 @@ +package ch.usi.inf.sa4.sanmarinoes.smarthut.models; + +/** A device that can be turned either on or off */ +public interface Switchable { + + /** + * Returns whether the device is on (true) or not (false) + * + * @return whether the device is on (true) or not (false) + */ + boolean isOn(); + + /** + * Sets the on status of the device + * + * @param on the new on status: true for on, false for off + */ + void setOn(boolean on); + + /** Toggle between on are off state */ + default void toggle() { + setOn(!isOn()); + } +} From f52a38082ccddcb8fd4af7ca08b1f650f1dd254f Mon Sep 17 00:00:00 2001 From: Claudio Maggioni Date: Sat, 14 Mar 2020 16:59:56 +0100 Subject: [PATCH 09/21] General rewrite of device controllers. Now only output devices can be changes with a PUT call on their controller. Other devices use the generic controller. --- .../controller/ButtonDimmerController.java | 9 ++- .../controller/DimmableLightController.java | 20 ++++-- .../controller/KnobDimmerController.java | 9 ++- .../controller/MotionSensorController.java | 12 +--- .../controller/RegularLightController.java | 17 +++-- .../smarthut/controller/SensorController.java | 8 --- .../controller/SmartPlugController.java | 15 +++-- .../smarthut/controller/SwitchController.java | 64 ++++++++++++++++--- .../dto/DimmableLightSaveRequest.java | 11 ++++ ...est.java => GenericDeviceSaveReguest.java} | 2 +- .../smarthut/dto/KnobDimmerSaveRequest.java | 30 --------- .../smarthut/dto/MotionSensorSaveRequest.java | 51 --------------- .../smarthut/dto/SensorSaveRequest.java | 22 ------- .../smarthut/dto/SwitchOperationRequest.java | 35 ++++++++++ .../smarthut/dto/SwitchSaveRequest.java | 52 --------------- .../smarthut/models/ButtonDimmer.java | 3 - .../smarthut/models/Connector.java | 2 +- .../smarthut/models/DimmableLight.java | 16 ++++- .../models/DimmableLightRepository.java | 2 +- .../smarthut/models/InputDevice.java | 3 + .../smarthut/models/KnobDimmer.java | 3 - .../smarthut/models/RegularLight.java | 2 +- .../models/RegularLightRepository.java | 2 +- .../smarthut/models/SmartPlug.java | 2 +- .../smarthut/models/SmartPlugRepository.java | 2 +- .../sanmarinoes/smarthut/models/Switch.java | 19 ++++++ .../smarthut/models/Switchable.java | 35 ++++++++-- .../smarthut/models/SwitchableRepository.java | 7 ++ 28 files changed, 228 insertions(+), 227 deletions(-) rename src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/{ButtonDimmerSaveRequest.java => GenericDeviceSaveReguest.java} (94%) delete mode 100644 src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/KnobDimmerSaveRequest.java delete mode 100644 src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/MotionSensorSaveRequest.java create mode 100644 src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/SwitchOperationRequest.java delete mode 100644 src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/SwitchSaveRequest.java create mode 100644 src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/SwitchableRepository.java diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/ButtonDimmerController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/ButtonDimmerController.java index 92d14db..061c6b9 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/ButtonDimmerController.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/ButtonDimmerController.java @@ -3,7 +3,7 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.controller; import static ch.usi.inf.sa4.sanmarinoes.smarthut.utils.Utils.toList; import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.ButtonDimmerDimRequest; -import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.ButtonDimmerSaveRequest; +import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.GenericDeviceSaveReguest; import ch.usi.inf.sa4.sanmarinoes.smarthut.error.NotFoundException; import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*; import java.util.List; @@ -24,7 +24,10 @@ public class ButtonDimmerController @Autowired protected ButtonDimmerController( ButtonDimmerRepository inputRepository, DimmableLightRepository outputRepository) { - super(inputRepository, outputRepository, ButtonDimmer.CONNECTOR); + super( + inputRepository, + outputRepository, + DimmableLight.BUTTON_DIMMER_DIMMABLE_LIGHT_CONNECTOR); this.buttonDimmerRepository = inputRepository; this.dimmableLightRepository = outputRepository; } @@ -40,7 +43,7 @@ public class ButtonDimmerController } @PostMapping - public ButtonDimmer create(@Valid @RequestBody final ButtonDimmerSaveRequest bd) { + public ButtonDimmer create(@Valid @RequestBody final GenericDeviceSaveReguest bd) { ButtonDimmer newBD = new ButtonDimmer(); newBD.setName(bd.getName()); newBD.setRoomId(bd.getRoomId()); diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/DimmableLightController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/DimmableLightController.java index 898b035..dd2e1e3 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/DimmableLightController.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/DimmableLightController.java @@ -29,14 +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.setName(dl.getName()); - newDL.setRoomId(dl.getRoomId()); + return save(new DimmableLight(), dl); + } - return dimmableLightService.save(newDL); + @PutMapping + public DimmableLight update(@Valid @RequestBody DimmableLightSaveRequest sp) + throws NotFoundException { + return save( + dimmableLightService.findById(sp.getId()).orElseThrow(NotFoundException::new), sp); } @DeleteMapping("/{id}") diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/KnobDimmerController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/KnobDimmerController.java index 4d51733..9f59889 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/KnobDimmerController.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/KnobDimmerController.java @@ -2,8 +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.GenericDeviceSaveReguest; import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.KnobDimmerDimRequest; -import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.KnobDimmerSaveRequest; import ch.usi.inf.sa4.sanmarinoes.smarthut.error.NotFoundException; import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*; import java.util.List; @@ -25,7 +25,10 @@ public class KnobDimmerController @Autowired protected KnobDimmerController( KnobDimmerRepository inputRepository, DimmableLightRepository outputRepository) { - super(inputRepository, outputRepository, KnobDimmer.CONNECTOR); + super( + inputRepository, + outputRepository, + DimmableLight.KNOB_DIMMER_DIMMABLE_LIGHT_CONNECTOR); this.knobDimmerRepository = inputRepository; this.dimmableLightRepository = outputRepository; } @@ -41,7 +44,7 @@ public class KnobDimmerController } @PostMapping - public KnobDimmer create(@Valid @RequestBody KnobDimmerSaveRequest kd) { + public KnobDimmer create(@Valid @RequestBody GenericDeviceSaveReguest kd) { KnobDimmer newKD = new KnobDimmer(); newKD.setName(kd.getName()); newKD.setRoomId(kd.getRoomId()); diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/MotionSensorController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/MotionSensorController.java index c0ba65a..4ba03b1 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/MotionSensorController.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/MotionSensorController.java @@ -2,7 +2,7 @@ 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; @@ -30,22 +30,14 @@ 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) { - ms.setId(0); - return this.create(ms); - } - @DeleteMapping("/{id}") public void delete(@PathVariable("id") long id) { motionSensorService.deleteById(id); diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/RegularLightController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/RegularLightController.java index acffb30..15ce2e8 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/RegularLightController.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/RegularLightController.java @@ -36,10 +36,7 @@ public class RegularLightController { return regularLightService.findById(id).orElseThrow(NotFoundException::new); } - @PostMapping - public RegularLight create(@Valid @RequestBody RegularLightSaveRequest rl) { - RegularLight newRL = new RegularLight(); - newRL.setId(rl.getId()); + private RegularLight save(RegularLight newRL, RegularLightSaveRequest rl) { newRL.setName(rl.getName()); newRL.setRoomId(rl.getRoomId()); newRL.setOn(rl.isOn()); @@ -47,10 +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) { - rl.setId(0); - return this.create(rl); + public RegularLight update(@Valid @RequestBody RegularLightSaveRequest rl) + throws NotFoundException { + return save( + regularLightService.findById(rl.getId()).orElseThrow(NotFoundException::new), rl); } @DeleteMapping("/{id}") diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SensorController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SensorController.java index cceb05a..3412e0c 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SensorController.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SensorController.java @@ -33,20 +33,12 @@ 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()); return sensorRepository.save(newSensor); } - @PutMapping - public Sensor update(@Valid @RequestBody SensorSaveRequest s) { - s.setId(0); - return this.create(s); - } - @DeleteMapping("/{id}") public void deleteById(@PathVariable("id") long id) { sensorRepository.deleteById(id); diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SmartPlugController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SmartPlugController.java index e05ce72..8a57430 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SmartPlugController.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SmartPlugController.java @@ -29,9 +29,7 @@ public class SmartPlugController { return smartPlugRepository.findById(id).orElseThrow(NotFoundException::new); } - @PostMapping - public SmartPlug create(@Valid @RequestBody SmartPlugSaveRequest sp) { - SmartPlug newSP = new SmartPlug(); + private SmartPlug save(SmartPlug newSP, SmartPlugSaveRequest sp) { newSP.setOn(sp.isOn()); newSP.setId(sp.getId()); newSP.setName(sp.getName()); @@ -40,10 +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) { - sp.setId(0); - return this.create(sp); + public SmartPlug update(@Valid @RequestBody SmartPlugSaveRequest sp) throws NotFoundException { + return save( + smartPlugRepository.findById(sp.getId()).orElseThrow(NotFoundException::new), sp); } @DeleteMapping("/{id}") diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SwitchController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SwitchController.java index 9166e15..f90108c 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SwitchController.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SwitchController.java @@ -2,7 +2,8 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.controller; import static ch.usi.inf.sa4.sanmarinoes.smarthut.utils.Utils.toList; -import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.SwitchSaveRequest; +import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.GenericDeviceSaveReguest; +import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.SwitchOperationRequest; import ch.usi.inf.sa4.sanmarinoes.smarthut.error.NotFoundException; import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*; import java.util.*; @@ -15,9 +16,24 @@ import org.springframework.web.bind.annotation.*; @RestController @EnableAutoConfiguration @RequestMapping("/switch") -public class SwitchController { +public class SwitchController extends InputDeviceConnectionController { - @Autowired private SwitchRepository switchRepository; + private SwitchRepository switchRepository; + private SwitchableRepository switchableRepository; + + /** + * Contstructs the controller by requiring essential object for the controller implementation + * + * @param inputRepository the input device repository + * @param outputRepository the output device repository + */ + @Autowired + protected SwitchController( + SwitchRepository inputRepository, SwitchableRepository outputRepository) { + super(inputRepository, outputRepository, Switchable.SWITCH_SWITCHABLE_CONNECTOR); + this.switchRepository = inputRepository; + this.switchableRepository = outputRepository; + } @GetMapping public List findAll() { @@ -30,20 +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) { - s.setId(0); - return this.create(s); + @PutMapping("/operate") + public Set operate(@Valid @RequestBody final SwitchOperationRequest sr) + throws NotFoundException { + final Switch s = switchRepository.findById(sr.getId()).orElseThrow(NotFoundException::new); + + switch (sr.getType()) { + case ON: + s.setOn(true); + break; + case OFF: + s.setOn(false); + break; + case TOGGLE: + s.toggle(); + break; + } + + switchableRepository.saveAll(s.getOutputs()); + + return s.getOutputs(); + } + + @PostMapping("/{id}/lights") + public Set addSwitchable( + @PathVariable("id") long inputId, @RequestParam("switchableId") Long switchableId) + throws NotFoundException { + return addOutput(inputId, switchableId); + } + + @DeleteMapping("/{id}/lights") + public Set removeSwitchable( + @PathVariable("id") long inputId, @RequestParam("switchableId") Long switchableId) + throws NotFoundException { + return removeOutput(inputId, switchableId); } @DeleteMapping("/{id}") diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/DimmableLightSaveRequest.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/DimmableLightSaveRequest.java index 03e3eb0..01bec1a 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/DimmableLightSaveRequest.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/DimmableLightSaveRequest.java @@ -6,6 +6,9 @@ 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) @@ -44,4 +47,12 @@ public class DimmableLightSaveRequest { public void setIntensity(Integer intensity) { this.intensity = intensity; } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/ButtonDimmerSaveRequest.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/GenericDeviceSaveReguest.java similarity index 94% rename from src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/ButtonDimmerSaveRequest.java rename to src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/GenericDeviceSaveReguest.java index bb0b0df..8ec2671 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/ButtonDimmerSaveRequest.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/GenericDeviceSaveReguest.java @@ -2,7 +2,7 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.dto; import javax.validation.constraints.NotNull; -public class ButtonDimmerSaveRequest { +public class GenericDeviceSaveReguest { /** * The room this device belongs in, as a foreign key id. To use when updating and inserting from * a REST call. diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/KnobDimmerSaveRequest.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/KnobDimmerSaveRequest.java deleted file mode 100644 index b1c0075..0000000 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/KnobDimmerSaveRequest.java +++ /dev/null @@ -1,30 +0,0 @@ -package ch.usi.inf.sa4.sanmarinoes.smarthut.dto; - -import javax.validation.constraints.NotNull; - -public class KnobDimmerSaveRequest { - /** - * The room this device belongs in, as a foreign key id. To use when updating and inserting from - * a REST call. - */ - @NotNull private Long roomId; - - /** The name of the device as assigned by the user (e.g. 'Master bedroom light') */ - @NotNull private String name; - - public void setRoomId(Long roomId) { - this.roomId = roomId; - } - - public void setName(String name) { - this.name = name; - } - - public Long getRoomId() { - return roomId; - } - - public String getName() { - return name; - } -} diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/MotionSensorSaveRequest.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/MotionSensorSaveRequest.java deleted file mode 100644 index a44c343..0000000 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/MotionSensorSaveRequest.java +++ /dev/null @@ -1,51 +0,0 @@ -package ch.usi.inf.sa4.sanmarinoes.smarthut.dto; - -import javax.validation.constraints.NotNull; - -public class MotionSensorSaveRequest { - private boolean detected; - - /** Device identifier */ - private long id; - - /** - * The room this device belongs in, as a foreign key id. To use when updating and inserting from - * a REST call. - */ - @NotNull private Long roomId; - - /** The name of the device as assigned by the user (e.g. 'Master bedroom light') */ - @NotNull private String name; - - public void setRoomId(Long roomId) { - this.roomId = roomId; - } - - public void setName(String name) { - this.name = name; - } - - public long getId() { - return id; - } - - public Long getRoomId() { - return roomId; - } - - public String getName() { - return name; - } - - public boolean isDetected() { - return detected; - } - - public void setDetected(boolean detected) { - this.detected = detected; - } - - public void setId(long id) { - this.id = id; - } -} diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/SensorSaveRequest.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/SensorSaveRequest.java index 0985db2..e9a5c68 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/SensorSaveRequest.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/SensorSaveRequest.java @@ -23,17 +23,11 @@ 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; - /** * The room this device belongs in, as a foreign key id. To use when updating and inserting from * a REST call. @@ -51,10 +45,6 @@ public class SensorSaveRequest { this.name = name; } - public long getId() { - return id; - } - public Long getRoomId() { return roomId; } @@ -70,16 +60,4 @@ public class SensorSaveRequest { public void setSensor(Sensor.SensorType sensor) { this.sensor = sensor; } - - public int getValue() { - return this.value; - } - - public void setValue(int newValue) { - this.value = newValue; - } - - public void setId(long id) { - this.id = id; - } } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/SwitchOperationRequest.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/SwitchOperationRequest.java new file mode 100644 index 0000000..3fb552b --- /dev/null +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/SwitchOperationRequest.java @@ -0,0 +1,35 @@ +package ch.usi.inf.sa4.sanmarinoes.smarthut.dto; + +import javax.validation.constraints.NotNull; + +/** An on/off/toggle operation on a switch */ +public class SwitchOperationRequest { + + /** The device id */ + @NotNull private Long id; + + public enum OperationType { + ON, + OFF, + TOGGLE + } + + /** The type of switch operation */ + @NotNull private SwitchOperationRequest.OperationType type; + + public OperationType getType() { + return type; + } + + public void setType(OperationType type) { + this.type = type; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } +} diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/SwitchSaveRequest.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/SwitchSaveRequest.java deleted file mode 100644 index 88050c0..0000000 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/SwitchSaveRequest.java +++ /dev/null @@ -1,52 +0,0 @@ -package ch.usi.inf.sa4.sanmarinoes.smarthut.dto; - -import javax.validation.constraints.NotNull; - -public class SwitchSaveRequest { - /** The state of this switch */ - private boolean on; - - /** Device identifier */ - private long id; - - /** - * The room this device belongs in, as a foreign key id. To use when updating and inserting from - * a REST call. - */ - @NotNull private Long roomId; - - /** The name of the device as assigned by the user (e.g. 'Master bedroom light') */ - @NotNull private String name; - - public void setRoomId(Long roomId) { - this.roomId = roomId; - } - - public void setName(String name) { - this.name = name; - } - - public long getId() { - return id; - } - - public Long getRoomId() { - return roomId; - } - - public String getName() { - return name; - } - - public boolean isOn() { - return on; - } - - public void setOn(boolean on) { - this.on = on; - } - - public void setId(long id) { - this.id = id; - } -} diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/ButtonDimmer.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/ButtonDimmer.java index 85d4fe0..fc91c22 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/ButtonDimmer.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/ButtonDimmer.java @@ -9,9 +9,6 @@ import javax.persistence.Entity; @Entity public class ButtonDimmer extends Dimmer { - public static final Connector CONNECTOR = - Connector.basic(ButtonDimmer::getOutputs, DimmableLight::setDimmerId); - /** The delta amount to apply to a increase or decrease intensity */ private static final int DIM_INCREMENT = 10; diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Connector.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Connector.java index dd68b10..91b1e88 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Connector.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Connector.java @@ -33,7 +33,7 @@ public interface Connector { * @return a Connector implementation for the pair of types J and K */ static Connector basic( - Function> outputsGetter, BiConsumer inputSetter) { + Function> outputsGetter, BiConsumer inputSetter) { return (i, o, connect) -> { if (connect) { outputsGetter.apply(i).add(o); diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/DimmableLight.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/DimmableLight.java index 277a7e9..9a75471 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/DimmableLight.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/DimmableLight.java @@ -11,7 +11,14 @@ import javax.validation.constraints.NotNull; /** Represent a dimmable light */ @Entity -public class DimmableLight extends OutputDevice implements Switchable { +public class DimmableLight extends Switchable { + + public static final Connector + BUTTON_DIMMER_DIMMABLE_LIGHT_CONNECTOR = + Connector.basic(ButtonDimmer::getOutputs, DimmableLight::setDimmerId); + + public static final Connector KNOB_DIMMER_DIMMABLE_LIGHT_CONNECTOR = + Connector.basic(KnobDimmer::getOutputs, DimmableLight::setDimmerId); public DimmableLight() { super("light"); @@ -65,5 +72,12 @@ public class DimmableLight extends OutputDevice implements Switchable { public void setDimmerId(Long dimmerId) { this.dimmerId = dimmerId; + super.setSwitchId(null); }; + + @Override + public void setSwitchId(Long switchId) { + super.setSwitchId(switchId); + this.dimmerId = null; + } } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/DimmableLightRepository.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/DimmableLightRepository.java index 484084b..a32b3c6 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/DimmableLightRepository.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/DimmableLightRepository.java @@ -1,3 +1,3 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.models; -public interface DimmableLightRepository extends DeviceRepository {} +public interface DimmableLightRepository extends SwitchableRepository {} diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/InputDevice.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/InputDevice.java index ad96347..da45b67 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/InputDevice.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/InputDevice.java @@ -2,12 +2,15 @@ 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); diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/KnobDimmer.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/KnobDimmer.java index d4eea0e..ce3745c 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/KnobDimmer.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/KnobDimmer.java @@ -9,9 +9,6 @@ import javax.persistence.Entity; @Entity public class KnobDimmer extends Dimmer { - public static final Connector CONNECTOR = - Connector.basic(KnobDimmer::getOutputs, DimmableLight::setDimmerId); - public KnobDimmer() { super("knob-dimmer"); } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/RegularLight.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/RegularLight.java index 02b5f35..de9c10a 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/RegularLight.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/RegularLight.java @@ -6,7 +6,7 @@ import javax.validation.constraints.NotNull; /** Represents a standard non-dimmable light */ @Entity -public class RegularLight extends OutputDevice implements Switchable { +public class RegularLight extends Switchable { /** Whether the light is on or not */ @Column(name = "light_on", nullable = false) diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/RegularLightRepository.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/RegularLightRepository.java index 43c4e17..cad8831 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/RegularLightRepository.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/RegularLightRepository.java @@ -1,3 +1,3 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.models; -public interface RegularLightRepository extends DeviceRepository {} +public interface RegularLightRepository extends SwitchableRepository {} diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/SmartPlug.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/SmartPlug.java index 6a77a13..fe936b3 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/SmartPlug.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/SmartPlug.java @@ -6,7 +6,7 @@ import javax.validation.constraints.NotNull; /** A smart plug that can be turned either on or off */ @Entity -public class SmartPlug extends OutputDevice implements Switchable { +public class SmartPlug extends Switchable { /** Whether the smart plug is on */ @Column(name = "smart_plug_on", nullable = false) diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/SmartPlugRepository.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/SmartPlugRepository.java index 0b2fd344..08d145d 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/SmartPlugRepository.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/SmartPlugRepository.java @@ -1,3 +1,3 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.models; -public interface SmartPlugRepository extends DeviceRepository {} +public interface SmartPlugRepository extends SwitchableRepository {} diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Switch.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Switch.java index 2576d38..d819dfe 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Switch.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Switch.java @@ -1,12 +1,18 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.models; +import java.util.HashSet; +import java.util.Set; import javax.persistence.Column; import javax.persistence.Entity; +import javax.persistence.OneToMany; /** A switch input device */ @Entity public class Switch extends InputDevice { + @OneToMany(mappedBy = "switchDevice") + private Set switchables = new HashSet<>(); + /** The state of this switch */ @Column(nullable = false, name = "switch_on") private boolean on; @@ -22,6 +28,15 @@ public class Switch extends InputDevice { */ public void setOn(boolean state) { on = state; + + for (final Switchable s : switchables) { + s.setOn(on); + } + } + + /** Toggle between on and off state */ + public void toggle() { + setOn(!isOn()); } /** @@ -32,4 +47,8 @@ public class Switch extends InputDevice { public boolean isOn() { return on; } + + public Set getOutputs() { + return switchables; + } } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Switchable.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Switchable.java index e13eea1..5ba0702 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Switchable.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Switchable.java @@ -1,24 +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 */ -public interface Switchable { +@Entity +@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) +public abstract class Switchable extends OutputDevice { + + public static final Connector SWITCH_SWITCHABLE_CONNECTOR = + Connector.basic(Switch::getOutputs, Switchable::setSwitchId); + + @ManyToOne + @GsonExclude + @JoinColumn(name = "switch_id", updatable = false, insertable = false) + private Switch switchDevice; + + @Column(name = "switch_id") + private Long switchId; + + protected Switchable(String kind) { + super(kind); + } /** * Returns whether the device is on (true) or not (false) * * @return whether the device is on (true) or not (false) */ - boolean isOn(); + public abstract boolean isOn(); /** * Sets the on status of the device * * @param on the new on status: true for on, false for off */ - void setOn(boolean on); + public abstract void setOn(boolean on); - /** Toggle between on are off state */ - default void toggle() { - setOn(!isOn()); + public Long getSwitchId() { + return switchId; + } + + public void setSwitchId(Long switchId) { + this.switchId = switchId; } } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/SwitchableRepository.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/SwitchableRepository.java new file mode 100644 index 0000000..589542d --- /dev/null +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/SwitchableRepository.java @@ -0,0 +1,7 @@ +package ch.usi.inf.sa4.sanmarinoes.smarthut.models; + +/** + * SwitchableRepository acts as a superclass for the other repositories so to mirror in the database + * the class inheritance present among the various switchable devices. + */ +public interface SwitchableRepository extends DeviceRepository {} From 5683bbe3e370116e2e4eeeb101cbb98a84855e12 Mon Sep 17 00:00:00 2001 From: Claudio Maggioni Date: Sat, 14 Mar 2020 19:51:01 +0100 Subject: [PATCH 10/21] Added room icons in model as enum --- .../smarthut/controller/RoomController.java | 2 +- .../smarthut/dto/RoomSaveRequest.java | 12 +- .../sa4/sanmarinoes/smarthut/models/Room.java | 112 +++++++++++++++++- 3 files changed, 115 insertions(+), 11 deletions(-) diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/RoomController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/RoomController.java index a5e6436..fb42037 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/RoomController.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/RoomController.java @@ -39,7 +39,7 @@ 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()); diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/RoomSaveRequest.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/RoomSaveRequest.java index f813b1c..ca8fa0f 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/RoomSaveRequest.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/RoomSaveRequest.java @@ -1,17 +1,19 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.dto; +import ch.usi.inf.sa4.sanmarinoes.smarthut.models.Room; import javax.persistence.Lob; import javax.validation.constraints.NotNull; public class RoomSaveRequest { + + @NotNull private Room.Icon icon; + /** - * Icon and image are to be given as byte[]. In order to get an encoded string from it, the + * Image is to be given as byte[]. In order to get an encoded string from it, the * Base64.getEncoder().encodeToString(byte[] content) should be used. For further information: * https://www.baeldung.com/java-base64-image-string * https://docs.oracle.com/javase/8/docs/api/java/util/Base64.html */ - @Lob private String icon; - @Lob private String image; /** The user given name of this room (e.g. 'Master bedroom') */ @@ -25,11 +27,11 @@ public class RoomSaveRequest { this.name = name; } - public String getIcon() { + public Room.Icon getIcon() { return icon; } - public void setIcon(String icon) { + public void setIcon(Room.Icon icon) { this.icon = icon; } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Room.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Room.java index 72859d5..c5b196a 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Room.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Room.java @@ -1,5 +1,6 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.models; +import com.google.gson.annotations.SerializedName; import io.swagger.annotations.ApiModelProperty; import javax.persistence.*; import javax.validation.constraints.NotNull; @@ -8,20 +9,121 @@ 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) @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 */ - @Column private String icon; - @Lob @Column(name = "image", columnDefinition = "TEXT") private String image; @@ -63,11 +165,11 @@ public class Room { this.name = name; } - public String getIcon() { + public Icon getIcon() { return icon; } - public void setIcon(String icon) { + public void setIcon(Icon icon) { this.icon = icon; } From 73e1328d7046ff37400bbc48f4d9d66a3076358b Mon Sep 17 00:00:00 2001 From: Claudio Maggioni Date: Sat, 14 Mar 2020 20:07:58 +0100 Subject: [PATCH 11/21] Branch cleaning --- build.gradle | 3 +-- .../smarthut/models/KnobDimmer.java | 6 +---- .../smarthut/websocket/HouseEndpoint.java | 24 ----------------- .../smarthut/websocket/WebSocketConfig.java | 26 ------------------- 4 files changed, 2 insertions(+), 57 deletions(-) delete mode 100644 src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/websocket/HouseEndpoint.java delete mode 100644 src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/websocket/WebSocketConfig.java diff --git a/build.gradle b/build.gradle index 4f4082f..c7b20f6 100644 --- a/build.gradle +++ b/build.gradle @@ -15,7 +15,6 @@ repositories { dependencies { compile 'org.hibernate.javax.persistence:hibernate-jpa-2.1-api:1.0.0.Final' - compile "org.springframework.boot:spring-boot-starter-websocket" implementation 'org.springframework.boot:spring-boot-starter' implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-security' @@ -25,7 +24,7 @@ dependencies { implementation 'org.postgresql:postgresql' compile "io.springfox:springfox-swagger2:2.9.2" compile group: 'io.springfox', name: 'springfox-swagger-ui', version: '2.9.2' - + implementation('org.springframework.boot:spring-boot-starter-web') { exclude group: 'org.springframework.boot', module: 'spring-boot-starter-json' } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/KnobDimmer.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/KnobDimmer.java index 7f4056b..0a90998 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/KnobDimmer.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/KnobDimmer.java @@ -33,11 +33,7 @@ public class KnobDimmer extends Dimmer { dl.setIntensity((dl.getIntensity() + 5) % 105); } else { dl.setIntensity(dl.getIntensity() + (5 - remainder)); - if (dl.getIntensity() == 0) { - dl.setIntensity(100); - } else { - dl.setIntensity(dl.getIntensity() - 5); - } + dl.setIntensity((dl.getIntensity() - 5) % 105); } } } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/websocket/HouseEndpoint.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/websocket/HouseEndpoint.java deleted file mode 100644 index 2e6de11..0000000 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/websocket/HouseEndpoint.java +++ /dev/null @@ -1,24 +0,0 @@ -package ch.usi.inf.sa4.sanmarinoes.smarthut.websocket; - -import java.io.IOException; -import javax.websocket.*; -import javax.websocket.server.ServerEndpoint; - -@ServerEndpoint( - value = - "theEndpointMustHaveAShotNameBecauseAndreaBritesMartesMaronesIsAVeryNiceProgrammerThatMustTypeThisByHand.exe") // DONE: choose path -public class HouseEndpoint { - private Session session; - - @OnOpen - public void onOpen(Session session) throws IOException {} - - @OnMessage - public void onMessage() throws IOException {} - - @OnClose - public void onClose() {} - - @OnError - public void onError() {} -} diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/websocket/WebSocketConfig.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/websocket/WebSocketConfig.java deleted file mode 100644 index d5ea39a..0000000 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/websocket/WebSocketConfig.java +++ /dev/null @@ -1,26 +0,0 @@ -package ch.usi.inf.sa4.sanmarinoes.smarthut.websocket; - -import org.springframework.context.annotation.Configuration; -import org.springframework.messaging.simp.config.MessageBrokerRegistry; -import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker; -import org.springframework.web.socket.config.annotation.StompEndpointRegistry; -import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer; - -@Configuration -@EnableWebSocketMessageBroker -public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { - - @Override - public void registerStompEndpoints(StompEndpointRegistry registry) { - registry.addEndpoint("/house") - .setAllowedOrigins("domainURL") - .withSockJS(); // TODO: set domain URL - } - - /** Creates message brokers in-memory to send and receive messages for topic and queue route */ - @Override - public void configureMessageBroker(MessageBrokerRegistry config) { - config.enableSimpleBroker("/topic/", "/queue/"); - config.setApplicationDestinationPrefixes("/app"); - } -} From 589ef8c3cc4de247c65d59717d4b10004427a720 Mon Sep 17 00:00:00 2001 From: Claudio Maggioni Date: Mon, 9 Mar 2020 23:38:57 +0100 Subject: [PATCH 12/21] Imported code from secret source for websockets (@tommi27 you don't know anything about it, right?) --- .../smarthut/socket/SensorSocketConfig.java | 26 ++++++++++++ .../smarthut/socket/SensorSocketDecoder.java | 32 +++++++++++++++ .../smarthut/socket/SensorSocketEncoder.java | 24 +++++++++++ .../smarthut/socket/SensorSocketEndpoint.java | 41 +++++++++++++++++++ 4 files changed, 123 insertions(+) create mode 100644 src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/SensorSocketConfig.java create mode 100644 src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/SensorSocketDecoder.java create mode 100644 src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/SensorSocketEncoder.java create mode 100644 src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/SensorSocketEndpoint.java diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/SensorSocketConfig.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/SensorSocketConfig.java new file mode 100644 index 0000000..7d04538 --- /dev/null +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/SensorSocketConfig.java @@ -0,0 +1,26 @@ +package ch.usi.inf.sa4.sanmarinoes.smarthut.socket; + +import javax.websocket.server.ServerEndpointConfig; + +public class SensorSocketConfig extends ServerEndpointConfig.Configurator { + + public static SensorSocketEndpoint getInstance() { + return instance; + } + + private static SensorSocketEndpoint instance = new SensorSocketEndpoint(); + + @Override + public T getEndpointInstance(Class endpointClass) throws InstantiationException { + try { + @SuppressWarnings("unchecked") + final T instance = (T) SensorSocketConfig.instance; + return instance; + } catch (ClassCastException e) { + final var e2 = + new InstantiationException("Cannot cast SensorSocketEndpoint to desired type"); + e2.initCause(e); + throw e2; + } + } +} diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/SensorSocketDecoder.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/SensorSocketDecoder.java new file mode 100644 index 0000000..ad3a76e --- /dev/null +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/SensorSocketDecoder.java @@ -0,0 +1,32 @@ +package ch.usi.inf.sa4.sanmarinoes.smarthut.socket; + +import com.google.gson.Gson; +import com.google.gson.JsonObject; +import com.google.gson.JsonSyntaxException; +import javax.websocket.*; + +public class SensorSocketDecoder implements Decoder.Text { + private Gson decoder; + + @Override + public void init(EndpointConfig endpointConfig) { + decoder = new Gson(); + } + + @Override + public void destroy() {} + + @Override + public JsonObject decode(String s) throws DecodeException { + try { + return decoder.fromJson(s, JsonObject.class); + } catch (JsonSyntaxException e) { + throw new DecodeException(s, "Cannot decode sensor message", e); + } + } + + @Override + public boolean willDecode(String s) { + return true; + } +} diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/SensorSocketEncoder.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/SensorSocketEncoder.java new file mode 100644 index 0000000..be04293 --- /dev/null +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/SensorSocketEncoder.java @@ -0,0 +1,24 @@ +package ch.usi.inf.sa4.sanmarinoes.smarthut.socket; + +import com.google.gson.Gson; +import com.google.gson.JsonObject; +import javax.websocket.EncodeException; +import javax.websocket.Encoder; +import javax.websocket.EndpointConfig; + +public class SensorSocketEncoder implements Encoder.Text { + private Gson encoder; + + @Override + public String encode(JsonObject object) throws EncodeException { + return encoder.toJson(object); + } + + @Override + public void init(EndpointConfig endpointConfig) { + encoder = new Gson(); + } + + @Override + public void destroy() {} +} diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/SensorSocketEndpoint.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/SensorSocketEndpoint.java new file mode 100644 index 0000000..82ab767 --- /dev/null +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/SensorSocketEndpoint.java @@ -0,0 +1,41 @@ +package ch.usi.inf.sa4.sanmarinoes.smarthut.socket; + +import com.google.gson.JsonObject; +import java.io.IOException; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import javax.websocket.*; +import javax.websocket.server.ServerEndpoint; + +@ServerEndpoint( + value = "/service", + configurator = SensorSocketConfig.class, + encoders = SensorSocketEncoder.class, + decoders = SensorSocketDecoder.class) +public class SensorSocketEndpoint { + + private Set clients = Collections.synchronizedSet(new HashSet<>()); + + public Set getClients() { + return clients; + } + + @OnOpen + public void onOpen(Session userSession) { + clients.add(userSession); + } + + @OnClose + public void onClose(Session userSession) { + clients.remove(userSession); + } + + public int broadcast(JsonObject message) throws IOException, EncodeException { + for (Session session : clients) { + System.out.println(message); + session.getBasicRemote().sendObject(message); + } + return clients.size(); + } +} From 707291e637adcd70781dbd8496f41356780a1027 Mon Sep 17 00:00:00 2001 From: Claudio Maggioni Date: Wed, 11 Mar 2020 16:23:58 +0100 Subject: [PATCH 13/21] Unauthenticated socket works --- build.gradle | 2 + gradle.yml | 39 +++++++++++++++++++ .../smarthut/config/WebSecurityConfig.java | 1 + .../smarthut/socket/SensorSocketConfig.java | 15 +++++++ .../smarthut/socket/SensorSocketDecoder.java | 32 --------------- .../smarthut/socket/SensorSocketEncoder.java | 24 ------------ .../smarthut/socket/SensorSocketEndpoint.java | 37 ++++++++---------- test.html | 26 +++++++++++++ 8 files changed, 100 insertions(+), 76 deletions(-) create mode 100644 gradle.yml delete mode 100644 src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/SensorSocketDecoder.java delete mode 100644 src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/SensorSocketEncoder.java create mode 100644 test.html diff --git a/build.gradle b/build.gradle index d23270b..5140ea7 100644 --- a/build.gradle +++ b/build.gradle @@ -23,6 +23,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' diff --git a/gradle.yml b/gradle.yml new file mode 100644 index 0000000..51fefa4 --- /dev/null +++ b/gradle.yml @@ -0,0 +1,39 @@ +# vim: set ts=2 sw=2 et tw=80: +image: gradle:jdk13 + +stages: + - build + - test + - deploy + +smarthut_build: + stage: build + script: + - gradle assemble + artifacts: + paths: + - build/libs/*.jar + expire_in: 1 week + +smarthut_test: + stage: test + script: + - gradle check + +smarthut_deploy: + stage: deploy + image: docker:latest + services: + - docker:dind + variables: + DOCKER_DRIVER: overlay + before_script: + - docker version + - docker info + - docker login -u smarthutsm -p $CI_DOCKER_PASS + script: + - "docker build -t smarthutsm/smarthut:${CI_COMMIT_BRANCH} --pull ." + - "docker push smarthutsm/smarthut:${CI_COMMIT_BRANCH}" + after_script: + - docker logout + diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/WebSecurityConfig.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/WebSecurityConfig.java index 253998d..ec116c3 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/WebSecurityConfig.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/WebSecurityConfig.java @@ -51,6 +51,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { // dont authenticate this particular request .authorizeRequests() .antMatchers( + "/sensor-socket", "/auth/login", "/swagger-ui.html", "/register", diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/SensorSocketConfig.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/SensorSocketConfig.java index 7d04538..4e032bf 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/SensorSocketConfig.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/SensorSocketConfig.java @@ -1,7 +1,12 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.socket; import javax.websocket.server.ServerEndpointConfig; +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; +@Configuration public class SensorSocketConfig extends ServerEndpointConfig.Configurator { public static SensorSocketEndpoint getInstance() { @@ -10,6 +15,16 @@ public class SensorSocketConfig extends ServerEndpointConfig.Configurator { private static SensorSocketEndpoint instance = new SensorSocketEndpoint(); + @Bean + public ServerEndpointRegistration sensorSocketEndpoint() { + return new ServerEndpointRegistration("/sensor-socket", instance); + } + + @Bean + public ServerEndpointExporter endpointExporter() { + return new ServerEndpointExporter(); + } + @Override public T getEndpointInstance(Class endpointClass) throws InstantiationException { try { diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/SensorSocketDecoder.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/SensorSocketDecoder.java deleted file mode 100644 index ad3a76e..0000000 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/SensorSocketDecoder.java +++ /dev/null @@ -1,32 +0,0 @@ -package ch.usi.inf.sa4.sanmarinoes.smarthut.socket; - -import com.google.gson.Gson; -import com.google.gson.JsonObject; -import com.google.gson.JsonSyntaxException; -import javax.websocket.*; - -public class SensorSocketDecoder implements Decoder.Text { - private Gson decoder; - - @Override - public void init(EndpointConfig endpointConfig) { - decoder = new Gson(); - } - - @Override - public void destroy() {} - - @Override - public JsonObject decode(String s) throws DecodeException { - try { - return decoder.fromJson(s, JsonObject.class); - } catch (JsonSyntaxException e) { - throw new DecodeException(s, "Cannot decode sensor message", e); - } - } - - @Override - public boolean willDecode(String s) { - return true; - } -} diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/SensorSocketEncoder.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/SensorSocketEncoder.java deleted file mode 100644 index be04293..0000000 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/SensorSocketEncoder.java +++ /dev/null @@ -1,24 +0,0 @@ -package ch.usi.inf.sa4.sanmarinoes.smarthut.socket; - -import com.google.gson.Gson; -import com.google.gson.JsonObject; -import javax.websocket.EncodeException; -import javax.websocket.Encoder; -import javax.websocket.EndpointConfig; - -public class SensorSocketEncoder implements Encoder.Text { - private Gson encoder; - - @Override - public String encode(JsonObject object) throws EncodeException { - return encoder.toJson(object); - } - - @Override - public void init(EndpointConfig endpointConfig) { - encoder = new Gson(); - } - - @Override - public void destroy() {} -} diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/SensorSocketEndpoint.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/SensorSocketEndpoint.java index 82ab767..36565c8 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/SensorSocketEndpoint.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/SensorSocketEndpoint.java @@ -1,19 +1,14 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.socket; +import com.google.gson.Gson; import com.google.gson.JsonObject; import java.io.IOException; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; +import java.util.*; import javax.websocket.*; -import javax.websocket.server.ServerEndpoint; -@ServerEndpoint( - value = "/service", - configurator = SensorSocketConfig.class, - encoders = SensorSocketEncoder.class, - decoders = SensorSocketDecoder.class) -public class SensorSocketEndpoint { +public class SensorSocketEndpoint extends Endpoint { + + private Gson gson = new Gson(); private Set clients = Collections.synchronizedSet(new HashSet<>()); @@ -21,16 +16,6 @@ public class SensorSocketEndpoint { return clients; } - @OnOpen - public void onOpen(Session userSession) { - clients.add(userSession); - } - - @OnClose - public void onClose(Session userSession) { - clients.remove(userSession); - } - public int broadcast(JsonObject message) throws IOException, EncodeException { for (Session session : clients) { System.out.println(message); @@ -38,4 +23,16 @@ public class SensorSocketEndpoint { } return clients.size(); } + + @Override + public void onOpen(Session session, EndpointConfig config) { + final JsonObject test = new JsonObject(); + test.addProperty("ciao", "mamma"); + try { + session.getBasicRemote().sendText(gson.toJson(test)); + clients.add(session); + } catch (IOException e) { + e.printStackTrace(); + } + } } diff --git a/test.html b/test.html new file mode 100644 index 0000000..42bb82d --- /dev/null +++ b/test.html @@ -0,0 +1,26 @@ + + + + + + + + From 34dce5457576e3ee9acb1e629c103f96a6d439ba Mon Sep 17 00:00:00 2001 From: tommi27 Date: Sat, 14 Mar 2020 20:17:47 +0100 Subject: [PATCH 14/21] wip --- .../smarthut/socket/SensorSocketEndpoint.java | 38 ++++++++++++++++--- 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/SensorSocketEndpoint.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/SensorSocketEndpoint.java index 36565c8..2fde5df 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/SensorSocketEndpoint.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/SensorSocketEndpoint.java @@ -1,27 +1,41 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.socket; +import ch.usi.inf.sa4.sanmarinoes.smarthut.config.JWTTokenUtil; +import ch.usi.inf.sa4.sanmarinoes.smarthut.models.User; import com.google.gson.Gson; import com.google.gson.JsonObject; import java.io.IOException; import java.util.*; import javax.websocket.*; +import org.springframework.beans.factory.annotation.Autowired; public class SensorSocketEndpoint extends Endpoint { private Gson gson = new Gson(); - private Set clients = Collections.synchronizedSet(new HashSet<>()); + @Autowired private JWTTokenUtil jwtTokenUtil; - public Set getClients() { - return clients; + private Set unauthorizedClients = Collections.synchronizedSet(new HashSet()); + + // commented out because of script not letting me push + // private Map< User, Set > authorizedClients = Collections.synchronizedMap( + // new HashMap< User, HashSet > + // ); + + public Set getUnauthorizedClients() { + return unauthorizedClients; + } + + public Map> getAuthorizedClients() { + return authorizedClients; } public int broadcast(JsonObject message) throws IOException, EncodeException { - for (Session session : clients) { + for (Session session : unauthorizedClients) { System.out.println(message); session.getBasicRemote().sendObject(message); } - return clients.size(); + return unauthorizedClients.size(); } @Override @@ -30,9 +44,21 @@ public class SensorSocketEndpoint extends Endpoint { test.addProperty("ciao", "mamma"); try { session.getBasicRemote().sendText(gson.toJson(test)); - clients.add(session); + unauthorizedClients.add(session); } catch (IOException e) { e.printStackTrace(); } } + + @OnMessage + public void onMessage(String message) { + if (message != null) { + if (message.contains("Bearer: ")) { + String token = message.substring(message.lastIndexOf("Bearer ")); + String username = jwtTokenUtil.getUsernameFromToken(token); + } else { + + } + } + } } From 3c034f56d11fd197208358a3bfb0d3897634884c Mon Sep 17 00:00:00 2001 From: Claudio Maggioni Date: Sun, 15 Mar 2020 10:44:10 +0100 Subject: [PATCH 15/21] Socket authentication works --- socket_test.html | 36 +++++++ .../smarthut/config/JWTRequestFilter.java | 17 ++-- .../smarthut/config/JWTTokenUtil.java | 72 -------------- .../smarthut/config/JWTTokenUtils.java | 84 ++++++++++++++++ .../controller/AuthenticationController.java | 10 +- .../socket/AuthenticationMessageListener.java | 96 +++++++++++++++++++ .../smarthut/socket/SensorSocketConfig.java | 25 +++-- .../smarthut/socket/SensorSocketEndpoint.java | 92 +++++++++++------- .../sa4/sanmarinoes/smarthut/utils/Utils.java | 17 +++- test.html | 26 ----- 10 files changed, 320 insertions(+), 155 deletions(-) create mode 100644 socket_test.html delete mode 100644 src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/JWTTokenUtil.java create mode 100644 src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/JWTTokenUtils.java create mode 100644 src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/AuthenticationMessageListener.java delete mode 100644 test.html diff --git a/socket_test.html b/socket_test.html new file mode 100644 index 0000000..90feba2 --- /dev/null +++ b/socket_test.html @@ -0,0 +1,36 @@ + + + + + +
+

Waiting for authentication...

+
+ + + diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/JWTRequestFilter.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/JWTRequestFilter.java index 853083b..e0cbb6a 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/JWTRequestFilter.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/JWTRequestFilter.java @@ -20,7 +20,7 @@ public class JWTRequestFilter extends OncePerRequestFilter { @Autowired private JWTUserDetailsService jwtUserDetailsService; - @Autowired private JWTTokenUtil jwtTokenUtil; + @Autowired private JWTTokenUtils jwtTokenUtils; @Override protected void doFilterInternal( @@ -30,13 +30,11 @@ public class JWTRequestFilter extends OncePerRequestFilter { String username = null; String jwtToken = null; - // JWT Token is in th - // e form "Bearer token". Remove Bearer word and get - // only the Token + // JWT Token is in the form "Bearer token". Remove Bearer word and get only the Token if (requestTokenHeader != null && requestTokenHeader.startsWith("Bearer ")) { jwtToken = requestTokenHeader.substring(7); try { - username = jwtTokenUtil.getUsernameFromToken(jwtToken); + username = jwtTokenUtils.getUsernameFromToken(jwtToken); } catch (IllegalArgumentException e) { System.out.println("Unable to get JWT Token"); } catch (ExpiredJwtException e) { @@ -44,14 +42,15 @@ public class JWTRequestFilter extends OncePerRequestFilter { } } else { logger.warn("JWT Token does not begin with Bearer String"); - } // Once we get the token validate it. + } + + // Once we get the token validate it. if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) { UserDetails userDetails = this.jwtUserDetailsService.loadUserByUsername( username); // if token is valid configure Spring Security to manually - // set - // authentication - if (jwtTokenUtil.validateToken(jwtToken, userDetails)) { + // set authentication + if (jwtTokenUtils.validateToken(jwtToken, userDetails)) { UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken( userDetails, null, userDetails.getAuthorities()); diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/JWTTokenUtil.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/JWTTokenUtil.java deleted file mode 100644 index 40d369f..0000000 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/JWTTokenUtil.java +++ /dev/null @@ -1,72 +0,0 @@ -package ch.usi.inf.sa4.sanmarinoes.smarthut.config; - -import io.jsonwebtoken.Claims; -import io.jsonwebtoken.Jwts; -import io.jsonwebtoken.SignatureAlgorithm; -import java.util.Date; -import java.util.HashMap; -import java.util.Map; -import java.util.function.Function; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.stereotype.Component; - -@Component -public class JWTTokenUtil { - public static final long JWT_TOKEN_VALIDITY = 5 * 60 * 60; - - @Value("${jwt.secret}") - private String secret; - - // retrieve username from jwt token - public String getUsernameFromToken(String token) { - return getClaimFromToken(token, Claims::getSubject); - } - - // retrieve expiration date from jwt token - public Date getExpirationDateFromToken(String token) { - return getClaimFromToken(token, Claims::getExpiration); - } - - public T getClaimFromToken(String token, Function claimsResolver) { - final Claims claims = getAllClaimsFromToken(token); - return claimsResolver.apply(claims); - } - - // for retrieveing any information from token we will need the secret key - private Claims getAllClaimsFromToken(String token) { - return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody(); - } // check if the token has expired - - private Boolean isTokenExpired(String token) { - final Date expiration = getExpirationDateFromToken(token); - return expiration.before(new Date()); - } // generate token for user - - public String generateToken(UserDetails userDetails) { - Map claims = new HashMap<>(); - return doGenerateToken(claims, userDetails.getUsername()); - } - - // while creating the token - - // 1. Define claims of the token, like Issuer, Expiration, Subject, and the ID - // 2. Sign the JWT using the HS512 algorithm and secret key. - // 3. According to JWS Compact - // Serialization(https://tools.ietf.org/html/draft-ietf-jose-json-web-signature-41#section-3.1) - // compaction of the JWT to a URL-safe string - private String doGenerateToken(Map claims, String subject) { - return Jwts.builder() - .setClaims(claims) - .setSubject(subject) - .setIssuedAt(new Date(System.currentTimeMillis())) - .setExpiration(new Date(System.currentTimeMillis() + JWT_TOKEN_VALIDITY * 1000)) - .signWith(SignatureAlgorithm.HS512, secret) - .compact(); - } - - // validate token - public Boolean validateToken(String token, UserDetails userDetails) { - final String username = getUsernameFromToken(token); - return (username.equals(userDetails.getUsername()) && !isTokenExpired(token)); - } -} diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/JWTTokenUtils.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/JWTTokenUtils.java new file mode 100644 index 0000000..f6943a8 --- /dev/null +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/JWTTokenUtils.java @@ -0,0 +1,84 @@ +package ch.usi.inf.sa4.sanmarinoes.smarthut.config; + +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; +import java.util.Date; +import java.util.HashMap; +import java.util.function.Function; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.stereotype.Component; + +/** A utility class to handle JWTs */ +@Component +public class JWTTokenUtils { + /** The duration in seconds of the validity of a single token */ + private static final long JWT_TOKEN_VALIDITY = 5 * 60 * 60; + + /** The secret key used to encrypt all JWTs */ + @Value("${jwt.secret}") + private String secret; + + /** + * Retrieves the claimed username from a given token + * + * @param token the token to inspect + * @return the username + */ + public String getUsernameFromToken(String token) { + return getClaimFromToken(token, Claims::getSubject); + } + + /** + * Returns whether the token given is expired or not + * + * @param token the given token + * @return true if expired, false if not + */ + public Boolean isTokenExpired(String token) { + final Date expiration = getClaimFromToken(token, Claims::getExpiration); + return expiration.before(new Date()); + } + + /** + * Creates a new JWT for a given user. While creating the token - 1. Define claims of the token, + * like Issuer, Expiration, Subject, and the ID 2. Sign the JWT using the HS512 algorithm and + * secret key. 3. According to JWS Compact Serialization + * (https://tools.ietf.org/html/draft-ietf-jose-json-web-signature-41#section-3.1) compaction of + * the JWT to a URL-safe string + * + * @param user the user to which create a JWT + * @return the newly generated token + */ + public String generateToken(UserDetails user) { + return Jwts.builder() + .setClaims(new HashMap<>()) + .setSubject(user.getUsername()) + .setIssuedAt(new Date(System.currentTimeMillis())) + .setExpiration(new Date(System.currentTimeMillis() + JWT_TOKEN_VALIDITY * 1000)) + .signWith(SignatureAlgorithm.HS512, secret) + .compact(); + } + + /** + * Validates the token given against matching userDetails + * + * @param token the token given + * @param userDetails user details to validate against + * @return true if valid, false if not + */ + public Boolean validateToken(String token, UserDetails userDetails) { + final String username = getUsernameFromToken(token); + return (username.equals(userDetails.getUsername()) && !isTokenExpired(token)); + } + + private T getClaimFromToken(String token, Function claimsResolver) { + final Claims claims = getAllClaimsFromToken(token); + return claimsResolver.apply(claims); + } + + private Claims getAllClaimsFromToken(String token) { + return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody(); + } +} diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/AuthenticationController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/AuthenticationController.java index 1a1e266..3160e1c 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/AuthenticationController.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/AuthenticationController.java @@ -1,6 +1,6 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.controller; -import ch.usi.inf.sa4.sanmarinoes.smarthut.config.JWTTokenUtil; +import ch.usi.inf.sa4.sanmarinoes.smarthut.config.JWTTokenUtils; import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.JWTRequest; import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.JWTResponse; import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.UserUpdateRequest; @@ -26,7 +26,7 @@ public class AuthenticationController { private final UserRepository userRepository; - private final JWTTokenUtil jwtTokenUtil; + private final JWTTokenUtils jwtTokenUtils; private final JWTUserDetailsService userDetailsService; @@ -35,11 +35,11 @@ public class AuthenticationController { public AuthenticationController( AuthenticationManager authenticationManager, UserRepository userRepository, - JWTTokenUtil jwtTokenUtil, + JWTTokenUtils jwtTokenUtils, JWTUserDetailsService userDetailsService) { this.authenticationManager = authenticationManager; this.userRepository = userRepository; - this.jwtTokenUtil = jwtTokenUtil; + this.jwtTokenUtils = jwtTokenUtils; this.userDetailsService = userDetailsService; } @@ -68,7 +68,7 @@ public class AuthenticationController { authenticationRequest.getUsernameOrEmail()); } - final String token = jwtTokenUtil.generateToken(userDetails); + final String token = jwtTokenUtils.generateToken(userDetails); return new JWTResponse(token); } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/AuthenticationMessageListener.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/AuthenticationMessageListener.java new file mode 100644 index 0000000..e0b9249 --- /dev/null +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/AuthenticationMessageListener.java @@ -0,0 +1,96 @@ +package ch.usi.inf.sa4.sanmarinoes.smarthut.socket; + +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 = new Gson(); + + private JWTTokenUtils jwtTokenUtils; + + private UserRepository userRepository; + + @Autowired + public AuthenticationMessageListener( + JWTTokenUtils jwtTokenUtils, UserRepository userRepository) { + this.jwtTokenUtils = jwtTokenUtils; + this.userRepository = userRepository; + } + + /** + * Generates a new message handler to handle socket authentication + * + * @param session the session to which authentication must be checked + * @param authorizedSetter function to call once user is authenticated + * @return a new message handler to handle socket authentication + */ + MessageHandler.Whole newHandler( + final Session session, BiConsumer authorizedSetter) { + return new MessageHandler.Whole<>() { + @Override + public void onMessage(final String message) { + System.out.println(message); + + if (message == null) { + acknowledge(false); + return; + } + + String token; + String username; + + try { + token = gson.fromJson(message, JsonObject.class).get("token").getAsString(); + username = jwtTokenUtils.getUsernameFromToken(token); + } catch (ExpiredJwtException e) { + System.err.println(e.getMessage()); + acknowledge(false); + return; + } catch (Throwable ignored) { + System.out.println("Token format not valid"); + acknowledge(false); + return; + } + + final User user = userRepository.findByUsername(username); + if (user == null || jwtTokenUtils.isTokenExpired(token)) { + System.out.println("Token not valid"); + acknowledge(false); + return; + } + + // Here user is authenticated + session.removeMessageHandler(this); + + // Add user-session pair in authorized list + authorizedSetter.accept(user, session); + + // update client to acknowledge authentication + acknowledge(true); + } + + private void acknowledge(boolean success) { + try { + session.getBasicRemote() + .sendText(gson.toJson(Map.of("authenticated", success))); + } catch (IOException e) { + e.printStackTrace(); + } + } + }; + } +} diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/SensorSocketConfig.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/SensorSocketConfig.java index 4e032bf..503667a 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/SensorSocketConfig.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/SensorSocketConfig.java @@ -1,25 +1,38 @@ 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 { - public static SensorSocketEndpoint getInstance() { - return instance; + private SensorSocketEndpoint instance; + + @Autowired + public SensorSocketConfig(SensorSocketEndpoint instance) { + this.instance = instance; } - private static SensorSocketEndpoint instance = new SensorSocketEndpoint(); - + /** + * Registers the sensor socket endpoint to the url /sensor-socket + * + * @return an endpoint registration object + */ @Bean - public ServerEndpointRegistration sensorSocketEndpoint() { + public ServerEndpointRegistration serverEndpointRegistration() { return new ServerEndpointRegistration("/sensor-socket", instance); } + /** + * Returns a new ServerEndpointExporter + * + * @return a new ServerEndpointExporter + */ @Bean public ServerEndpointExporter endpointExporter() { return new ServerEndpointExporter(); @@ -29,7 +42,7 @@ public class SensorSocketConfig extends ServerEndpointConfig.Configurator { public T getEndpointInstance(Class endpointClass) throws InstantiationException { try { @SuppressWarnings("unchecked") - final T instance = (T) SensorSocketConfig.instance; + final T instance = (T) this.instance; return instance; } catch (ClassCastException e) { final var e2 = diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/SensorSocketEndpoint.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/SensorSocketEndpoint.java index 2fde5df..d40e874 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/SensorSocketEndpoint.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/SensorSocketEndpoint.java @@ -1,64 +1,84 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.socket; -import ch.usi.inf.sa4.sanmarinoes.smarthut.config.JWTTokenUtil; +import static ch.usi.inf.sa4.sanmarinoes.smarthut.utils.Utils.didThrow; + 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 com.google.gson.JsonObject; -import java.io.IOException; import java.util.*; import javax.websocket.*; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +/** Endpoint of socket at URL /sensor-socket used to update the client with sensor information */ +@Component public class SensorSocketEndpoint extends Endpoint { private Gson gson = new Gson(); - @Autowired private JWTTokenUtil jwtTokenUtil; + private AuthenticationMessageListener authenticationMessageListener; - private Set unauthorizedClients = Collections.synchronizedSet(new HashSet()); + private Set unauthorizedClients = Collections.synchronizedSet(new HashSet<>()); - // commented out because of script not letting me push - // private Map< User, Set > authorizedClients = Collections.synchronizedMap( - // new HashMap< User, HashSet > - // ); + private Multimap authorizedClients = + Multimaps.synchronizedMultimap(HashMultimap.create()); + @Autowired + public SensorSocketEndpoint(AuthenticationMessageListener authenticationMessageListener) { + this.authenticationMessageListener = authenticationMessageListener; + } + + /** + * Returns a synchronized set of socket sessions not yet authorized with a token + * + * @return a synchronized set of socket sessions not yet authorized with a token + */ public Set getUnauthorizedClients() { return unauthorizedClients; } - public Map> getAuthorizedClients() { + /** + * Returns a synchronized User to Session multimap with authorized sessions + * + * @return a synchronized User to Session multimap with authorized sessions + */ + public Multimap getAuthorizedClients() { return authorizedClients; } - public int broadcast(JsonObject message) throws IOException, EncodeException { - for (Session session : unauthorizedClients) { - System.out.println(message); - session.getBasicRemote().sendObject(message); - } - return unauthorizedClients.size(); + /** + * Given a message and a user, broadcasts that message in json to all associated clients and + * returns the number of successful transfers + * + * @param message the message to send + * @param u the user to which to send the message + * @return number of successful transfer + */ + public long broadcast(Object message, User u) { + final Collection sessions = authorizedClients.get(u); + return sessions.stream() + .parallel() + .filter(didThrow(s -> s.getAsyncRemote().sendObject(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) { - final JsonObject test = new JsonObject(); - test.addProperty("ciao", "mamma"); - try { - session.getBasicRemote().sendText(gson.toJson(test)); - unauthorizedClients.add(session); - } catch (IOException e) { - e.printStackTrace(); - } - } - - @OnMessage - public void onMessage(String message) { - if (message != null) { - if (message.contains("Bearer: ")) { - String token = message.substring(message.lastIndexOf("Bearer ")); - String username = jwtTokenUtil.getUsernameFromToken(token); - } else { - - } - } + unauthorizedClients.add(session); + session.addMessageHandler( + authenticationMessageListener.newHandler( + session, + (u, s) -> { + unauthorizedClients.remove(s); + authorizedClients.put(u, s); + })); } } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/utils/Utils.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/utils/Utils.java index d9fcf12..bc8719d 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/utils/Utils.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/utils/Utils.java @@ -1,14 +1,29 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.utils; import java.util.List; +import java.util.concurrent.Future; +import java.util.function.Function; +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() {} public static List toList(Iterable iterable) { return StreamSupport.stream(iterable.spliterator(), false).collect(Collectors.toList()); } + + public static Predicate didThrow(Function> consumer) { + return (t) -> { + try { + consumer.apply(t).get(); + return true; + } catch (Throwable e) { + System.err.println(e.getMessage()); + return false; + } + }; + } } diff --git a/test.html b/test.html deleted file mode 100644 index 42bb82d..0000000 --- a/test.html +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - From 5a441a69925ee8b7bfebc0a828b7d84dbbf6b459 Mon Sep 17 00:00:00 2001 From: Claudio Maggioni Date: Sun, 15 Mar 2020 13:41:57 +0100 Subject: [PATCH 16/21] Socket now sends sensor updates --- socket_test.html | 4 +++ .../smarthut/config/GsonConfig.java | 2 +- .../smarthut/controller/SensorController.java | 27 +++++++++++++++++++ .../smarthut/dto/SensorSaveRequest.java | 11 ++++++++ .../sanmarinoes/smarthut/models/Device.java | 6 +++++ .../smarthut/models/DeviceRepository.java | 21 +++++++++++++++ .../sa4/sanmarinoes/smarthut/models/Room.java | 6 +++++ .../sanmarinoes/smarthut/models/Sensor.java | 23 +++++++++++----- .../sa4/sanmarinoes/smarthut/models/User.java | 19 +++++++++++++ .../socket/AuthenticationMessageListener.java | 3 ++- .../smarthut/socket/SensorSocketEndpoint.java | 6 +++-- .../sa4/sanmarinoes/smarthut/utils/Utils.java | 12 ++++++--- 12 files changed, 126 insertions(+), 14 deletions(-) diff --git a/socket_test.html b/socket_test.html index 90feba2..687388b 100644 --- a/socket_test.html +++ b/socket_test.html @@ -1,3 +1,5 @@ + + @@ -25,6 +27,8 @@ connection.onmessage = function(evt) { ""; } else if (data.authenticated === false) { malusa.innerHTML = "

Authentication error

"; + } else { + malusa.innerHTML += "

" + JSON.stringify(JSON.parse(evt.data), null, 2) + "

"; } }; diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/GsonConfig.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/GsonConfig.java index e12d9f0..69c4fc9 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/GsonConfig.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/GsonConfig.java @@ -20,7 +20,7 @@ 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()); diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SensorController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SensorController.java index 3412e0c..400d06f 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SensorController.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SensorController.java @@ -5,6 +5,9 @@ import static ch.usi.inf.sa4.sanmarinoes.smarthut.utils.Utils.toList; import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.SensorSaveRequest; import ch.usi.inf.sa4.sanmarinoes.smarthut.error.NotFoundException; import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*; +import ch.usi.inf.sa4.sanmarinoes.smarthut.socket.SensorSocketEndpoint; +import java.math.BigDecimal; +import java.security.Principal; import java.util.*; import java.util.List; import javax.validation.Valid; @@ -19,6 +22,8 @@ public class SensorController { @Autowired private SensorRepository sensorRepository; + @Autowired private SensorSocketEndpoint sensorSocketEndpoint; + @GetMapping public List findAll() { return toList(sensorRepository.findAll()); @@ -35,10 +40,32 @@ public class SensorController { newSensor.setSensor(s.getSensor()); newSensor.setName(s.getName()); newSensor.setRoomId(s.getRoomId()); + newSensor.setValue(s.getValue()); return sensorRepository.save(newSensor); } + @PutMapping("/{id}/value") + public Sensor updateValue( + @PathVariable("id") Long sensorId, + @RequestParam("value") BigDecimal value, + final Principal principal) + throws NotFoundException { + + final Sensor sensor = + sensorRepository + .findByIdAndUsername(sensorId, principal.getName()) + .orElseThrow(NotFoundException::new); + sensor.setValue(value); + final Sensor toReturn = sensorRepository.save(sensor); + + System.out.println("sensor: " + sensor); + + sensorSocketEndpoint.broadcast(sensor, sensorRepository.findUser(sensorId)); + + return toReturn; + } + @DeleteMapping("/{id}") public void deleteById(@PathVariable("id") long id) { sensorRepository.deleteById(id); diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/SensorSaveRequest.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/SensorSaveRequest.java index e9a5c68..62b0b5e 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/SensorSaveRequest.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/SensorSaveRequest.java @@ -2,6 +2,7 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.dto; import ch.usi.inf.sa4.sanmarinoes.smarthut.models.Sensor; import com.google.gson.annotations.SerializedName; +import java.math.BigDecimal; import javax.persistence.EnumType; import javax.persistence.Enumerated; import javax.validation.constraints.NotNull; @@ -28,6 +29,8 @@ public class SensorSaveRequest { @Enumerated(value = EnumType.STRING) private Sensor.SensorType sensor; + @NotNull private BigDecimal value; + /** * The room this device belongs in, as a foreign key id. To use when updating and inserting from * a REST call. @@ -60,4 +63,12 @@ public class SensorSaveRequest { public void setSensor(Sensor.SensorType sensor) { this.sensor = sensor; } + + public BigDecimal getValue() { + return value; + } + + public void setValue(BigDecimal value) { + this.value = value; + } } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Device.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Device.java index e8a621b..c564914 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Device.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Device.java @@ -1,5 +1,6 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.models; +import ch.usi.inf.sa4.sanmarinoes.smarthut.config.GsonExclude; import com.google.gson.annotations.SerializedName; import io.swagger.annotations.ApiModelProperty; import javax.persistence.*; @@ -26,6 +27,11 @@ 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. diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/DeviceRepository.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/DeviceRepository.java index fdae66e..b90639b 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/DeviceRepository.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/DeviceRepository.java @@ -1,6 +1,8 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.models; import java.util.List; +import java.util.Optional; +import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.query.Param; @@ -10,4 +12,23 @@ import org.springframework.data.repository.query.Param; */ public interface DeviceRepository extends CrudRepository { List findByRoomId(@Param("roomId") long roomId); + + /** + * Finds devices by their id and a username + * + * @param id the device id + * @param username a User's username + * @return an optional device, empty if none found + */ + @Query("SELECT d FROM Device d JOIN d.room r JOIN r.user u WHERE d.id = ?1 AND u.username = ?2") + Optional findByIdAndUsername(Long id, String username); + + /** + * Find the user associated with a device through a room + * + * @param deviceId the device id + * @return a user object + */ + @Query("SELECT u FROM Device d JOIN d.room r JOIN r.user u WHERE d.id = ?1") + User findUser(Long deviceId); } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Room.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Room.java index e54b462..68c3fc0 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Room.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Room.java @@ -1,5 +1,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.*; @@ -128,6 +129,11 @@ public class Room { @Column(name = "image", columnDefinition = "TEXT") 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 * inserting from a REST call. diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Sensor.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Sensor.java index b1d98a2..6dd634a 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Sensor.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Sensor.java @@ -1,6 +1,8 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.models; import com.google.gson.annotations.SerializedName; +import java.math.BigDecimal; +import java.util.Map; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.EnumType; @@ -11,6 +13,12 @@ import javax.validation.constraints.NotNull; @Entity public class Sensor extends InputDevice { + private static final Map TYPICAL_VALUES = + Map.of( + SensorType.TEMPERATURE, 17, + SensorType.HUMIDITY, 40, + SensorType.LIGHT, 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) @@ -42,19 +50,22 @@ public class Sensor extends InputDevice { public void setSensor(SensorType sensor) { this.sensor = sensor; - - // TODO: setup hook for sockets live update } - public int getValue() { + public BigDecimal getValue() { return this.value; } - public void setValue(int newValue) { + public void setValue(BigDecimal newValue) { this.value = newValue; } public Sensor() { super("sensor"); } + + @Override + public String toString() { + return "Sensor{" + "value=" + value + ", sensor=" + sensor + '}'; + } } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/User.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/User.java index f1b88ca..dc6766d 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/User.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/User.java @@ -1,6 +1,7 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.models; import io.swagger.annotations.ApiModelProperty; +import java.util.Objects; import javax.persistence.*; /** A user of the Smarthut application */ @@ -105,4 +106,22 @@ public class User { + isEnabled + '}'; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + User user = (User) o; + return id.equals(user.id) + && name.equals(user.name) + && username.equals(user.username) + && password.equals(user.password) + && email.equals(user.email) + && isEnabled.equals(user.isEnabled); + } + + @Override + public int hashCode() { + return Objects.hash(id, name, username, password, email, isEnabled); + } } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/AuthenticationMessageListener.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/AuthenticationMessageListener.java index e0b9249..e3d97ea 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/AuthenticationMessageListener.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/AuthenticationMessageListener.java @@ -1,5 +1,6 @@ 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; @@ -18,7 +19,7 @@ import org.springframework.stereotype.Component; @Component public class AuthenticationMessageListener { - private Gson gson = new Gson(); + private Gson gson = GsonConfig.gson(); private JWTTokenUtils jwtTokenUtils; diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/SensorSocketEndpoint.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/SensorSocketEndpoint.java index d40e874..ee7ff56 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/SensorSocketEndpoint.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/SensorSocketEndpoint.java @@ -2,6 +2,7 @@ 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; @@ -16,7 +17,7 @@ import org.springframework.stereotype.Component; @Component public class SensorSocketEndpoint extends Endpoint { - private Gson gson = new Gson(); + private Gson gson = GsonConfig.gson(); private AuthenticationMessageListener authenticationMessageListener; @@ -58,9 +59,10 @@ public class SensorSocketEndpoint extends Endpoint { */ public long broadcast(Object message, User u) { final Collection sessions = authorizedClients.get(u); + System.out.println(authorizedClients + " " + sessions + " " + u); return sessions.stream() .parallel() - .filter(didThrow(s -> s.getAsyncRemote().sendObject(gson.toJson(message)))) + .filter(didThrow(s -> s.getBasicRemote().sendText(gson.toJson(message)))) .count(); } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/utils/Utils.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/utils/Utils.java index bc8719d..3f24d4c 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/utils/Utils.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/utils/Utils.java @@ -1,8 +1,6 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.utils; import java.util.List; -import java.util.concurrent.Future; -import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.StreamSupport; @@ -11,14 +9,20 @@ import java.util.stream.StreamSupport; public final class Utils { private Utils() {} + @FunctionalInterface + public interface ConsumerWithException { + void apply(T input) throws Throwable; + } + public static List toList(Iterable iterable) { return StreamSupport.stream(iterable.spliterator(), false).collect(Collectors.toList()); } - public static Predicate didThrow(Function> consumer) { + public static Predicate didThrow(ConsumerWithException consumer) { return (t) -> { try { - consumer.apply(t).get(); + consumer.apply(t); + System.out.println("successful"); return true; } catch (Throwable e) { System.err.println(e.getMessage()); From cf940df6b2e06687dea219b32fc1d868be29b73a Mon Sep 17 00:00:00 2001 From: Claudio Maggioni Date: Sun, 15 Mar 2020 13:51:14 +0100 Subject: [PATCH 17/21] Socket now sends motion sensor updates --- .../controller/MotionSensorController.java | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/MotionSensorController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/MotionSensorController.java index 4ba03b1..96ba755 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/MotionSensorController.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/MotionSensorController.java @@ -6,6 +6,8 @@ import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.GenericDeviceSaveReguest; import ch.usi.inf.sa4.sanmarinoes.smarthut.error.NotFoundException; import ch.usi.inf.sa4.sanmarinoes.smarthut.models.MotionSensor; import ch.usi.inf.sa4.sanmarinoes.smarthut.models.MotionSensorRepository; +import ch.usi.inf.sa4.sanmarinoes.smarthut.socket.SensorSocketEndpoint; +import java.security.Principal; import java.util.List; import javax.validation.Valid; import org.springframework.beans.factory.annotation.Autowired; @@ -19,6 +21,8 @@ public class MotionSensorController { @Autowired private MotionSensorRepository motionSensorService; + @Autowired private SensorSocketEndpoint sensorSocketEndpoint; + @GetMapping public List findAll() { return toList(motionSensorService.findAll()); @@ -38,6 +42,25 @@ public class MotionSensorController { return motionSensorService.save(newMS); } + @PutMapping("/{id}/detect") + public MotionSensor updateDetection( + @PathVariable("id") Long sensorId, + @RequestParam("detected") boolean detected, + final Principal principal) + throws NotFoundException { + + final MotionSensor sensor = + motionSensorService + .findByIdAndUsername(sensorId, principal.getName()) + .orElseThrow(NotFoundException::new); + sensor.setDetected(detected); + final MotionSensor toReturn = motionSensorService.save(sensor); + + sensorSocketEndpoint.broadcast(sensor, motionSensorService.findUser(sensorId)); + + return toReturn; + } + @DeleteMapping("/{id}") public void delete(@PathVariable("id") long id) { motionSensorService.deleteById(id); From 620c196393ff56fdb8ea1ffafa4d850549802ddc Mon Sep 17 00:00:00 2001 From: Claudio Maggioni Date: Sun, 15 Mar 2020 14:49:51 +0100 Subject: [PATCH 18/21] Implemented fake updates --- .../smarthut/SmarthutApplication.java | 2 + .../controller/MotionSensorController.java | 27 +++++--- .../smarthut/controller/SensorController.java | 30 +++++---- .../sanmarinoes/smarthut/models/Sensor.java | 8 +-- .../smarthut/scheduled/SensorUpdateTasks.java | 62 +++++++++++++++++++ .../socket/AuthenticationMessageListener.java | 2 - .../smarthut/socket/SensorSocketEndpoint.java | 1 - .../sa4/sanmarinoes/smarthut/utils/Utils.java | 1 - 8 files changed, 106 insertions(+), 27 deletions(-) create mode 100644 src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/scheduled/SensorUpdateTasks.java diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/SmarthutApplication.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/SmarthutApplication.java index 242f03f..57f7b42 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/SmarthutApplication.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/SmarthutApplication.java @@ -3,8 +3,10 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.scheduling.annotation.EnableScheduling; @SpringBootApplication +@EnableScheduling @EnableJpaRepositories("ch.usi.inf.sa4.sanmarinoes.smarthut.models") public class SmarthutApplication { public static void main(String[] args) { diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/MotionSensorController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/MotionSensorController.java index 96ba755..59c0343 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/MotionSensorController.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/MotionSensorController.java @@ -42,6 +42,22 @@ public class MotionSensorController { return motionSensorService.save(newMS); } + /** + * Updates detection status of given motion sensor and propagates update throgh socket + * + * @param sensor the motion sensor to update + * @param detected the new detection status + * @return the updated motion sensor + */ + public MotionSensor updateDetectionFromMotionSensor(MotionSensor sensor, boolean detected) { + sensor.setDetected(detected); + final MotionSensor toReturn = motionSensorService.save(sensor); + + sensorSocketEndpoint.broadcast(sensor, motionSensorService.findUser(sensor.getId())); + + return toReturn; + } + @PutMapping("/{id}/detect") public MotionSensor updateDetection( @PathVariable("id") Long sensorId, @@ -49,16 +65,11 @@ public class MotionSensorController { final Principal principal) throws NotFoundException { - final MotionSensor sensor = + return updateDetectionFromMotionSensor( motionSensorService .findByIdAndUsername(sensorId, principal.getName()) - .orElseThrow(NotFoundException::new); - sensor.setDetected(detected); - final MotionSensor toReturn = motionSensorService.save(sensor); - - sensorSocketEndpoint.broadcast(sensor, motionSensorService.findUser(sensorId)); - - return toReturn; + .orElseThrow(NotFoundException::new), + detected); } @DeleteMapping("/{id}") diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SensorController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SensorController.java index 400d06f..ee1be81 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SensorController.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SensorController.java @@ -45,25 +45,33 @@ public class SensorController { return sensorRepository.save(newSensor); } + /** + * Updates the sensor with new measurement and propagates update through websocket + * + * @param sensor the sensor to update + * @param value the new measurement + * @return the updated sensor + */ + public Sensor updateValueFromSensor(Sensor sensor, BigDecimal value) { + sensor.setValue(value); + final Sensor toReturn = sensorRepository.save(sensor); + + sensorSocketEndpoint.broadcast(sensor, sensorRepository.findUser(sensor.getId())); + + return toReturn; + } + @PutMapping("/{id}/value") public Sensor updateValue( @PathVariable("id") Long sensorId, @RequestParam("value") BigDecimal value, final Principal principal) throws NotFoundException { - - final Sensor sensor = + return updateValueFromSensor( sensorRepository .findByIdAndUsername(sensorId, principal.getName()) - .orElseThrow(NotFoundException::new); - sensor.setValue(value); - final Sensor toReturn = sensorRepository.save(sensor); - - System.out.println("sensor: " + sensor); - - sensorSocketEndpoint.broadcast(sensor, sensorRepository.findUser(sensorId)); - - return toReturn; + .orElseThrow(NotFoundException::new), + value); } @DeleteMapping("/{id}") diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Sensor.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Sensor.java index 6dd634a..525ceb3 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Sensor.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Sensor.java @@ -13,11 +13,11 @@ import javax.validation.constraints.NotNull; @Entity public class Sensor extends InputDevice { - private static final Map TYPICAL_VALUES = + public static final Map TYPICAL_VALUES = Map.of( - SensorType.TEMPERATURE, 17, - SensorType.HUMIDITY, 40, - SensorType.LIGHT, 1000); + 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 { diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/scheduled/SensorUpdateTasks.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/scheduled/SensorUpdateTasks.java new file mode 100644 index 0000000..b1614aa --- /dev/null +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/scheduled/SensorUpdateTasks.java @@ -0,0 +1,62 @@ +package ch.usi.inf.sa4.sanmarinoes.smarthut.scheduled; + +import ch.usi.inf.sa4.sanmarinoes.smarthut.controller.MotionSensorController; +import ch.usi.inf.sa4.sanmarinoes.smarthut.controller.SensorController; +import ch.usi.inf.sa4.sanmarinoes.smarthut.models.MotionSensorRepository; +import ch.usi.inf.sa4.sanmarinoes.smarthut.models.Sensor; +import ch.usi.inf.sa4.sanmarinoes.smarthut.models.SensorRepository; +import java.math.BigDecimal; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; +import java.util.stream.StreamSupport; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +/** Generates fake sensor (and motion sensor) updates as required by milestone one */ +@Component +public class SensorUpdateTasks { + + @Autowired private SensorRepository sensorRepository; + + @Autowired private MotionSensorRepository motionSensorRepository; + + @Autowired private SensorController sensorController; + + @Autowired private MotionSensorController motionSensorController; + + /** Generates fake sensor updates every two seconds with a +/- 1.25% error */ + @Scheduled(fixedRate = 2000) + public void sensorFakeUpdate() { + StreamSupport.stream(sensorRepository.findAll().spliterator(), true) + .forEach( + sensor -> + sensorController.updateValueFromSensor( + sensor, + Sensor.TYPICAL_VALUES + .get(sensor.getSensor()) + .multiply( + new BigDecimal( + 0.9875 + Math.random() / 40)))); + } + + /** + * Generate fake motion detections in all motion detectors every 20 seconds for 2 seconds at + * most + */ + @Scheduled(fixedDelay = 20000) + public void motionSensorFakeUpdate() { + StreamSupport.stream(motionSensorRepository.findAll().spliterator(), true) + .forEach( + sensor -> { + motionSensorController.updateDetectionFromMotionSensor(sensor, true); + CompletableFuture.delayedExecutor( + (long) (Math.random() * 2000), TimeUnit.MILLISECONDS) + .execute( + () -> + motionSensorController + .updateDetectionFromMotionSensor( + sensor, false)); + }); + } +} diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/AuthenticationMessageListener.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/AuthenticationMessageListener.java index e3d97ea..89415aa 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/AuthenticationMessageListener.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/AuthenticationMessageListener.java @@ -44,8 +44,6 @@ public class AuthenticationMessageListener { return new MessageHandler.Whole<>() { @Override public void onMessage(final String message) { - System.out.println(message); - if (message == null) { acknowledge(false); return; diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/SensorSocketEndpoint.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/SensorSocketEndpoint.java index ee7ff56..bc2f90e 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/SensorSocketEndpoint.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/SensorSocketEndpoint.java @@ -59,7 +59,6 @@ public class SensorSocketEndpoint extends Endpoint { */ public long broadcast(Object message, User u) { final Collection sessions = authorizedClients.get(u); - System.out.println(authorizedClients + " " + sessions + " " + u); return sessions.stream() .parallel() .filter(didThrow(s -> s.getBasicRemote().sendText(gson.toJson(message)))) diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/utils/Utils.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/utils/Utils.java index 3f24d4c..99d363b 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/utils/Utils.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/utils/Utils.java @@ -22,7 +22,6 @@ public final class Utils { return (t) -> { try { consumer.apply(t); - System.out.println("successful"); return true; } catch (Throwable e) { System.err.println(e.getMessage()); From 24fa574c63e16402c21d5d3ed9e28f41d74e3c89 Mon Sep 17 00:00:00 2001 From: Claudio Maggioni Date: Thu, 12 Mar 2020 16:57:50 +0100 Subject: [PATCH 19/21] Added email confirmation test --- build.gradle | 1 + .../models/ConfirmationTokenRepository.java | 2 + .../smarthut/AuthenticationTests.java | 36 ++++++++------- .../sanmarinoes/smarthut/SmartHutTest.java | 44 +++++++++++++++++++ src/test/resources/application.properties | 2 +- 5 files changed, 69 insertions(+), 16 deletions(-) diff --git a/build.gradle b/build.gradle index 5140ea7..303f5e7 100644 --- a/build.gradle +++ b/build.gradle @@ -32,6 +32,7 @@ dependencies { compile 'io.springfox:springfox-swagger2:2.9.2' compile 'io.springfox:springfox-swagger-ui:2.9.2' compile 'org.springframework.boot:spring-boot-configuration-processor' + testCompile 'org.springframework.boot:spring-boot-starter-webflux' implementation('org.springframework.boot:spring-boot-starter-web') { exclude group: 'org.springframework.boot', module: 'spring-boot-starter-json' diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/ConfirmationTokenRepository.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/ConfirmationTokenRepository.java index 9bf3791..40c6a17 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/ConfirmationTokenRepository.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/ConfirmationTokenRepository.java @@ -6,6 +6,8 @@ import org.springframework.data.repository.CrudRepository; public interface ConfirmationTokenRepository extends CrudRepository { ConfirmationToken findByConfirmationToken(String confirmationToken); + ConfirmationToken findByUser(User user); + @Transactional void deleteByUserAndResetPassword(User user, boolean resetPassword); } diff --git a/src/test/java/ch/usi/inf/sa4/sanmarinoes/smarthut/AuthenticationTests.java b/src/test/java/ch/usi/inf/sa4/sanmarinoes/smarthut/AuthenticationTests.java index 60761cd..d13104f 100644 --- a/src/test/java/ch/usi/inf/sa4/sanmarinoes/smarthut/AuthenticationTests.java +++ b/src/test/java/ch/usi/inf/sa4/sanmarinoes/smarthut/AuthenticationTests.java @@ -8,6 +8,8 @@ import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.OkResponse; import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.UserRegistrationRequest; import ch.usi.inf.sa4.sanmarinoes.smarthut.error.DuplicateRegistrationException; import ch.usi.inf.sa4.sanmarinoes.smarthut.error.UnauthorizedException; +import ch.usi.inf.sa4.sanmarinoes.smarthut.models.ConfirmationTokenRepository; +import ch.usi.inf.sa4.sanmarinoes.smarthut.models.UserRepository; import com.google.gson.JsonArray; import com.google.gson.JsonObject; import java.util.Map; @@ -25,6 +27,10 @@ public class AuthenticationTests extends SmartHutTest { @Autowired private TestRestTemplate restTemplate; + @Autowired private UserRepository userRepository; + + @Autowired private ConfirmationTokenRepository tokenRepository; + private UserRegistrationRequest getDisabledUser() { final UserRegistrationRequest disabledUser = new UserRegistrationRequest(); disabledUser.setName("Disabled User"); @@ -34,15 +40,6 @@ public class AuthenticationTests extends SmartHutTest { return disabledUser; } - private static final UserRegistrationRequest enabledUser = new UserRegistrationRequest(); - - static { - enabledUser.setName("Enabled User"); - enabledUser.setEmail("enabled@example.com"); - enabledUser.setUsername("enabled"); - enabledUser.setPassword("password"); - } - @Override protected void setUp() { final ResponseEntity res = @@ -50,12 +47,7 @@ public class AuthenticationTests extends SmartHutTest { this.url("/register"), getDisabledUser(), OkResponse.class); assertThat(res.getStatusCode().equals(HttpStatus.OK)); - final ResponseEntity res2 = - this.restTemplate.postForEntity( - this.url("/register"), enabledUser, OkResponse.class); - assertThat(res2.getStatusCode().equals(HttpStatus.OK)); - - // TODO: email confirmation for enabledUser + registerTestUser(restTemplate, userRepository, tokenRepository); } @Test @@ -230,4 +222,18 @@ public class AuthenticationTests extends SmartHutTest { assertThat(res.getBody() != null); assertThat(res.getBody().isUserDisabled()); } + + @Test + public void loginShouldReturnTokenWithEnabledUser() { + final JWTRequest request = new JWTRequest(); + request.setUsernameOrEmail("enabled"); + request.setPassword("password"); + + final ResponseEntity res = + this.restTemplate.postForEntity(url("/auth/login"), request, JWTResponse.class); + assertThat(res.getStatusCode().equals(HttpStatus.OK)); + assertThat(res.getBody() != null); + assertThat(res.getBody().getToken() != null); + assertThat(!res.getBody().getToken().isEmpty()); + } } diff --git a/src/test/java/ch/usi/inf/sa4/sanmarinoes/smarthut/SmartHutTest.java b/src/test/java/ch/usi/inf/sa4/sanmarinoes/smarthut/SmartHutTest.java index 5c6e097..f2b737a 100644 --- a/src/test/java/ch/usi/inf/sa4/sanmarinoes/smarthut/SmartHutTest.java +++ b/src/test/java/ch/usi/inf/sa4/sanmarinoes/smarthut/SmartHutTest.java @@ -1,6 +1,18 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut; +import static org.assertj.core.api.Assertions.assertThat; + +import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.OkResponse; +import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.UserRegistrationRequest; +import ch.usi.inf.sa4.sanmarinoes.smarthut.models.ConfirmationToken; +import ch.usi.inf.sa4.sanmarinoes.smarthut.models.ConfirmationTokenRepository; +import ch.usi.inf.sa4.sanmarinoes.smarthut.models.User; +import ch.usi.inf.sa4.sanmarinoes.smarthut.models.UserRepository; import org.junit.jupiter.api.BeforeEach; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.reactive.function.client.WebClient; public abstract class SmartHutTest { private boolean setupDone = false; @@ -15,6 +27,38 @@ public abstract class SmartHutTest { protected void setUp() {} + protected static final UserRegistrationRequest enabledUser = new UserRegistrationRequest(); + + static { + enabledUser.setName("Enabled User"); + enabledUser.setEmail("enabled@example.com"); + enabledUser.setUsername("enabled"); + enabledUser.setPassword("password"); + } + + protected void registerTestUser( + final TestRestTemplate restTemplate, + final UserRepository userRepository, + final ConfirmationTokenRepository tokenRepository) { + final ResponseEntity res2 = + restTemplate.postForEntity(this.url("/register"), enabledUser, OkResponse.class); + assertThat(res2.getStatusCode().equals(HttpStatus.OK)); + + final User persistedEnabledUser = userRepository.findByUsername("enabled"); + final ConfirmationToken token = tokenRepository.findByUser(persistedEnabledUser); + + final ResponseEntity res3 = + WebClient.create(getBaseURL()) + .get() + .uri("/register/confirm-account?token=" + token.getConfirmationToken()) + .retrieve() + .toBodilessEntity() + .block(); + + assertThat(res3.getStatusCode().is2xxSuccessful()); + assertThat(userRepository.findByUsername("enabled").getEnabled()); + } + @BeforeEach void setUpHack() { if (!setupDone) { diff --git a/src/test/resources/application.properties b/src/test/resources/application.properties index ce6fe39..673d02a 100644 --- a/src/test/resources/application.properties +++ b/src/test/resources/application.properties @@ -28,7 +28,7 @@ server.port = 2000 email.registrationSubject=Complete your SmartHut.sm registration email.registration=To confirm your registration, please click here: -email.registrationPath=http://localhost:8080/register/confirm-account?token= +email.registrationPath=http://localhost:2000/register/confirm-account?token= email.resetpasswordSubject=SmartHut.sm password reset email.resetpassword=To reset your password, please click here: From 9c60475e92d2f8c17f4003d06f98870ab229e7bd Mon Sep 17 00:00:00 2001 From: Claudio Maggioni Date: Sun, 15 Mar 2020 17:05:52 +0100 Subject: [PATCH 20/21] Added GET /device route --- .../smarthut/controller/DeviceController.java | 12 ++++++++---- .../smarthut/models/DeviceRepository.java | 9 +++++++++ 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/DeviceController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/DeviceController.java index fa3242d..2d8dd99 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/DeviceController.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/DeviceController.java @@ -6,13 +6,12 @@ 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.PutMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; @RestController @EnableAutoConfiguration @@ -22,6 +21,11 @@ public class DeviceController { @Autowired private DeviceRepository deviceRepository; @Autowired private RoomRepository roomRepository; + @GetMapping + public List getAll(final Principal user) { + return deviceRepository.findAllByUsername(user.getName()); + } + @PutMapping public Device update(@Valid @RequestBody DeviceSaveRequest deviceSaveRequest) throws NotFoundException, BadDataException { diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/DeviceRepository.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/DeviceRepository.java index b90639b..f844882 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/DeviceRepository.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/DeviceRepository.java @@ -23,6 +23,15 @@ public interface DeviceRepository extends CrudRepository findByIdAndUsername(Long id, String username); + /** + * Finds all devices belonging to a user + * + * @param username the User's username + * @return all devices of that user + */ + @Query("SELECT d FROM Device d JOIN d.room r JOIN r.user u WHERE u.username = ?1") + List findAllByUsername(String username); + /** * Find the user associated with a device through a room * From 23edc90b815cb896bf8adadd8674557e09d0fa80 Mon Sep 17 00:00:00 2001 From: Claudio Maggioni Date: Mon, 16 Mar 2020 14:53:07 +0100 Subject: [PATCH 21/21] Fixed @tommi27 uniqueness frenzy for rooms MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I have now discovered the Schadenfreude in using git-blame. To quote an old commit message from high school: [Trascrizione domande fisica e correzione di GRAVISSIMI bug presenti nel codici di Maggioni](https://gitlab.com/staccastacca/scuolatest/-/commit/dd0933def0262ce1ca2f2b006d4f7a364fb17272) ``` La trascrizione dei test è stata fatta in modo certosino, simile a quello dei monaci emanuensi. I GRAVISSIMI buggg (3 g perchè fa faigo) potevano rendere il sito incomprensibile ``` --- .gitlab-ci.yml | 2 ++ .../usi/inf/sa4/sanmarinoes/smarthut/models/Device.java | 8 ++------ .../ch/usi/inf/sa4/sanmarinoes/smarthut/models/Room.java | 2 +- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index fc62202..2250a03 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -11,6 +11,8 @@ stages: smarthut_deploy: stage: deploy image: docker:latest + tags: + - dind services: - docker:dind variables: diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Device.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Device.java index c564914..6d0333d 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Device.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Device.java @@ -49,17 +49,13 @@ public abstract class Device { * The name for the category of this particular device (e.g 'dimmer'). Not stored in the * database but set thanks to constructors */ - @ApiModelProperty(hidden = true) - @Transient - private final String kind; + @Transient private final String kind; /** * The way this device behaves in the automation flow. Not stored in the database but set thanks * to constructors */ - @ApiModelProperty(hidden = true) - @Transient - private final FlowType flowType; + @Transient private final FlowType flowType; public long getId() { return id; diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Room.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Room.java index 68c3fc0..a30e4f8 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Room.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Room.java @@ -139,7 +139,7 @@ public class Room { * inserting from a REST call. */ @NotNull - @Column(name = "user_id", nullable = false, unique = true) + @Column(name = "user_id", nullable = false) private Long userId; /** The user given name of this room (e.g. 'Master bedroom') */