Code review
This commit is contained in:
parent
6317ac99e4
commit
eef0887da1
6 changed files with 113 additions and 31 deletions
|
@ -23,15 +23,26 @@ public class GsonConfig {
|
|||
return converter;
|
||||
}
|
||||
|
||||
public static Gson gson() {
|
||||
private static GsonBuilder configureBuilder() {
|
||||
final GsonBuilder builder = new GsonBuilder();
|
||||
builder.registerTypeAdapter(Json.class, new SpringfoxJsonToGsonAdapter());
|
||||
builder.addSerializationExclusionStrategy(new AnnotationExclusionStrategy());
|
||||
RuntimeTypeAdapterFactory<State> runtimeTypeAdapterFactory =
|
||||
RuntimeTypeAdapterFactory.of(State.class, "kind")
|
||||
.registerSubtype(SwitchableState.class, "switchableState")
|
||||
.registerSubtype(DimmableState.class, "dimmableState");
|
||||
builder.registerTypeAdapterFactory(runtimeTypeAdapterFactory);
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static Gson gson() {
|
||||
final GsonBuilder builder = configureBuilder();
|
||||
builder.addSerializationExclusionStrategy(new AnnotationExclusionStrategy());
|
||||
return builder.create();
|
||||
}
|
||||
|
||||
public static Gson socketGson() {
|
||||
final GsonBuilder builder = configureBuilder();
|
||||
builder.addSerializationExclusionStrategy(new SocketAnnotationExclusionStrategy());
|
||||
return builder.create();
|
||||
}
|
||||
}
|
||||
|
@ -56,3 +67,16 @@ class AnnotationExclusionStrategy implements ExclusionStrategy {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/** GSON exclusion strategy to exclude attributes with @SocketGsonExclude */
|
||||
class SocketAnnotationExclusionStrategy implements ExclusionStrategy {
|
||||
@Override
|
||||
public boolean shouldSkipField(FieldAttributes f) {
|
||||
return f.getAnnotation(SocketGsonExclude.class) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldSkipClass(Class<?> clazz) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
package ch.usi.inf.sa4.sanmarinoes.smarthut.config;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
public @interface SocketGsonExclude {}
|
|
@ -6,6 +6,7 @@ import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.DimmableSaveRequest;
|
|||
import ch.usi.inf.sa4.sanmarinoes.smarthut.error.DuplicateStateException;
|
||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.error.NotFoundException;
|
||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*;
|
||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.service.DeviceService;
|
||||
import java.security.Principal;
|
||||
import java.util.List;
|
||||
import javax.validation.Valid;
|
||||
|
@ -19,37 +20,43 @@ import org.springframework.web.bind.annotation.*;
|
|||
public class DimmableLightController {
|
||||
|
||||
@Autowired private UserRepository userRepository;
|
||||
@Autowired private DimmableLightRepository dimmableLightService;
|
||||
@Autowired private DimmableLightRepository dimmableLightRepository;
|
||||
@Autowired private SceneRepository sceneRepository;
|
||||
@Autowired private StateRepository<State<?>> stateRepository;
|
||||
@Autowired private DeviceService deviceService;
|
||||
|
||||
@GetMapping
|
||||
public List<DimmableLight> findAll() {
|
||||
return toList(dimmableLightService.findAll());
|
||||
return toList(dimmableLightRepository.findAll());
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
public DimmableLight findById(@PathVariable("id") long id) throws NotFoundException {
|
||||
return dimmableLightService.findById(id).orElseThrow(NotFoundException::new);
|
||||
return dimmableLightRepository.findById(id).orElseThrow(NotFoundException::new);
|
||||
}
|
||||
|
||||
private DimmableLight save(DimmableLight initial, DimmableSaveRequest dl) {
|
||||
private DimmableLight save(DimmableLight initial, DimmableSaveRequest dl, String username) {
|
||||
initial.setIntensity(dl.getIntensity());
|
||||
initial.setName(dl.getName());
|
||||
initial.setRoomId(dl.getRoomId());
|
||||
|
||||
return dimmableLightService.save(initial);
|
||||
return deviceService.saveAsOwner(initial, username);
|
||||
}
|
||||
|
||||
/*
|
||||
Assume that only the host can create a device
|
||||
Here save always as host, but remember to propagate change to guests (DeviceService.saveAsOwner())
|
||||
*/
|
||||
@PostMapping
|
||||
public DimmableLight create(@Valid @RequestBody DimmableSaveRequest dl) {
|
||||
return save(new DimmableLight(), dl);
|
||||
public DimmableLight create(
|
||||
@Valid @RequestBody DimmableSaveRequest dl, final Principal principal) {
|
||||
return save(new DimmableLight(), dl, principal.getName());
|
||||
}
|
||||
|
||||
private DimmableLight fetchIfOwnerOrGuest(final Principal principal, Long id, Long hostId)
|
||||
throws NotFoundException {
|
||||
if (hostId == null) {
|
||||
return dimmableLightService
|
||||
return dimmableLightRepository
|
||||
.findByIdAndUsername(id, principal.getName())
|
||||
.orElseThrow(NotFoundException::new);
|
||||
} else {
|
||||
|
@ -57,11 +64,12 @@ public class DimmableLightController {
|
|||
* Slightly less extremely verbose check through various repositories to control user/guest authorization.
|
||||
*/
|
||||
DimmableLight dl =
|
||||
dimmableLightService
|
||||
dimmableLightRepository
|
||||
.findByIdAndUserId(id, hostId)
|
||||
.orElseThrow(NotFoundException::new);
|
||||
User host = userRepository.findById(hostId).orElseThrow(IllegalStateException::new);
|
||||
User guest = userRepository.findByUsername(principal.getName());
|
||||
dl.setFromHost(true);
|
||||
if (!host.getGuests().contains(guest)) {
|
||||
throw new NotFoundException();
|
||||
} else {
|
||||
|
@ -70,16 +78,21 @@ public class DimmableLightController {
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Here you must behave differently if hostId is given or not:
|
||||
- if not given, assume the owner of the device wants to update the device. In this case, save with DeviceService.saveAsOwner();
|
||||
- if given, assume a guest wants to update the intensity of this light. In this case, save with DeviceService.saveAsGuest();
|
||||
*/
|
||||
@PutMapping
|
||||
public DimmableLight update(
|
||||
@Valid @RequestBody DimmableSaveRequest sp, final Principal principal, Long hostId)
|
||||
throws NotFoundException {
|
||||
return save(fetchIfOwnerOrGuest(principal, sp.getId(), hostId), sp);
|
||||
return save(fetchIfOwnerOrGuest(principal, sp.getId(), hostId), sp, principal.getName());
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
public void delete(@PathVariable("id") long id) {
|
||||
dimmableLightService.deleteById(id);
|
||||
dimmableLightRepository.deleteById(id);
|
||||
}
|
||||
|
||||
// the full url should be: "/dimmableLight/{id}/state?sceneId={sceneId}
|
||||
|
@ -92,7 +105,7 @@ public class DimmableLightController {
|
|||
throws NotFoundException, DuplicateStateException {
|
||||
|
||||
DimmableLight d =
|
||||
dimmableLightService
|
||||
dimmableLightRepository
|
||||
.findByIdAndUsername(deviceId, principal.getName())
|
||||
.orElseThrow(NotFoundException::new);
|
||||
State<? extends Dimmable> s = d.cloneState();
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
|
||||
|
||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.config.GsonExclude;
|
||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.config.SocketGsonExclude;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import java.util.HashSet;
|
||||
|
@ -32,6 +33,7 @@ public abstract class Device {
|
|||
@ManyToOne
|
||||
@JoinColumn(name = "room_id", updatable = false, insertable = false)
|
||||
@GsonExclude
|
||||
@SocketGsonExclude
|
||||
private Room room;
|
||||
|
||||
/**
|
||||
|
@ -61,8 +63,15 @@ public abstract class Device {
|
|||
|
||||
@OneToMany(mappedBy = "device", orphanRemoval = true)
|
||||
@GsonExclude
|
||||
@SocketGsonExclude
|
||||
private Set<State<?>> states = new HashSet<>();
|
||||
|
||||
@Transient @GsonExclude private boolean fromHost = false;
|
||||
|
||||
public void setFromHost(boolean fromHost) {
|
||||
this.fromHost = fromHost;
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
package ch.usi.inf.sa4.sanmarinoes.smarthut.service;
|
||||
|
||||
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.User;
|
||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.UserRepository;
|
||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.socket.SensorSocketEndpoint;
|
||||
import java.util.Set;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class DeviceService {
|
||||
// FIXME: TO BE MERGED WITH USER STORY 5 (MATTEO'S STUFF)
|
||||
|
||||
@Autowired DeviceRepository<Device> deviceRepository;
|
||||
@Autowired UserRepository userRepository;
|
||||
@Autowired SensorSocketEndpoint endpoint;
|
||||
|
||||
/*
|
||||
TODO: remember to put a @Transient @GsonIgnore (but not @SocketGsonIgnore) property on device to signal a device update
|
||||
TODO: coming from DeviceService.saveAsGuest()
|
||||
*/
|
||||
|
||||
// TODO: create saveAsGuest(device, guestUsername, hostId)
|
||||
|
||||
public <T extends Device> T saveAsOwner(T device, String username) {
|
||||
final User user = userRepository.findByUsername(username);
|
||||
final Set<User> guests = user.getGuests();
|
||||
for (final User guest : guests) {
|
||||
// set set from host true
|
||||
// broadcast to endpoint the object device, with recieving user set to guest
|
||||
}
|
||||
}
|
||||
}
|
|
@ -9,25 +9,17 @@ import com.google.common.collect.HashMultimap;
|
|||
import com.google.common.collect.Multimap;
|
||||
import com.google.common.collect.Multimaps;
|
||||
import com.google.gson.Gson;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import javax.websocket.*;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import io.jsonwebtoken.ExpiredJwtException;
|
||||
import org.hibernate.annotations.common.reflection.XProperty;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.core.parameters.P;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* Endpoint of socket at URL /sensor-socket used to update the client with sensor information
|
||||
*/
|
||||
/** Endpoint of socket at URL /sensor-socket used to update the client with sensor information */
|
||||
@Component
|
||||
public class SensorSocketEndpoint extends Endpoint {
|
||||
|
||||
private Gson gson = GsonConfig.gson();
|
||||
private Gson gson = GsonConfig.socketGson();
|
||||
|
||||
private UserRepository userRepository;
|
||||
|
||||
|
@ -46,6 +38,7 @@ public class SensorSocketEndpoint extends Endpoint {
|
|||
|
||||
/**
|
||||
* Queues a single device update for a certain user to be sent
|
||||
*
|
||||
* @param device the device update to be sent
|
||||
* @param u the user the device belongs
|
||||
*/
|
||||
|
@ -56,9 +49,7 @@ public class SensorSocketEndpoint extends Endpoint {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends all device updates queued to be sent in a unique WebSocket message
|
||||
*/
|
||||
/** Sends all device updates queued to be sent in a unique WebSocket message */
|
||||
public void flushDeviceUpdates() {
|
||||
synchronized (messages) {
|
||||
for (Map.Entry<User, Map<Long, Device>> batchForUser : messages.entrySet()) {
|
||||
|
@ -69,11 +60,11 @@ public class SensorSocketEndpoint extends Endpoint {
|
|||
}
|
||||
|
||||
/**
|
||||
* Given a collection of messages and a user, broadcasts that message in json to all
|
||||
* associated clients
|
||||
* Given a collection of messages and a user, broadcasts that message in json to all associated
|
||||
* clients
|
||||
*
|
||||
* @param messages the message batch to send
|
||||
* @param u the user to which to send the message
|
||||
* @param u the user to which to send the message
|
||||
*/
|
||||
private void broadcast(User u, Collection<?> messages) {
|
||||
if (messages.isEmpty()) return;
|
||||
|
@ -95,7 +86,7 @@ public class SensorSocketEndpoint extends Endpoint {
|
|||
* Handles the opening of a socket session with a client
|
||||
*
|
||||
* @param session the newly born session
|
||||
* @param config endpoint configuration
|
||||
* @param config endpoint configuration
|
||||
*/
|
||||
@Override
|
||||
public void onOpen(Session session, EndpointConfig config) {
|
||||
|
|
Loading…
Reference in a new issue