Redux websockets implemented
This commit is contained in:
parent
a4b59dc922
commit
0805d9d4d0
5 changed files with 61 additions and 82 deletions
|
@ -3,6 +3,7 @@
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@giantmachines/redux-websocket": "^1.1.7",
|
||||||
"@material-ui/core": "^4.9.4",
|
"@material-ui/core": "^4.9.4",
|
||||||
"@material-ui/icons": "^4.9.1",
|
"@material-ui/icons": "^4.9.1",
|
||||||
"@testing-library/jest-dom": "^4.2.4",
|
"@testing-library/jest-dom": "^4.2.4",
|
||||||
|
|
20
smart-hut/src/endpoint.js
Normal file
20
smart-hut/src/endpoint.js
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
/**
|
||||||
|
* Returns the endpoint URL (SmartHut backend URL)
|
||||||
|
* @returns {String} endpoint URL
|
||||||
|
*/
|
||||||
|
export function endpointURL() {
|
||||||
|
return window.BACKEND_URL !== "__BACKEND_URL__"
|
||||||
|
? window.BACKEND_URL
|
||||||
|
: "http://localhost:8080";
|
||||||
|
}
|
||||||
|
|
||||||
|
export function socketURL(token) {
|
||||||
|
const httpURL = new URL(endpointURL());
|
||||||
|
const isSecure = httpURL.protocol === "https:";
|
||||||
|
const protocol = isSecure ? "wss:" : "ws:";
|
||||||
|
const port = httpURL.port || (isSecure ? 443 : 80);
|
||||||
|
const url = `${protocol}//${httpURL.hostname}:${port}/sensor-socket?token=${token}`;
|
||||||
|
console.log('socket url: ', url);
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
import smartHutStore from "./store";
|
import smartHutStore from "./store";
|
||||||
import actions from "./storeActions";
|
import actions from "./storeActions";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
import { endpointURL, socketURL } from "./endpoint";
|
||||||
|
import { connect, disconnect } from "@giantmachines/redux-websocket";
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An object returned by promise rejections in remoteservice
|
* An object returned by promise rejections in remoteservice
|
||||||
|
@ -16,16 +19,6 @@ class RemoteError extends Error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the endpoint URL (SmartHut backend URL)
|
|
||||||
* @returns {String} endpoint URL
|
|
||||||
*/
|
|
||||||
function endpointURL() {
|
|
||||||
return window.BACKEND_URL !== "__BACKEND_URL__"
|
|
||||||
? window.BACKEND_URL
|
|
||||||
: "http://localhost:8080";
|
|
||||||
}
|
|
||||||
|
|
||||||
const Endpoint = {
|
const Endpoint = {
|
||||||
axiosInstance: axios.create({
|
axiosInstance: axios.create({
|
||||||
baseURL: endpointURL(),
|
baseURL: endpointURL(),
|
||||||
|
@ -176,7 +169,10 @@ export const RemoteService = {
|
||||||
login: (usernameOrEmail, password) => {
|
login: (usernameOrEmail, password) => {
|
||||||
return (dispatch) => {
|
return (dispatch) => {
|
||||||
return Endpoint.login(usernameOrEmail, password)
|
return Endpoint.login(usernameOrEmail, password)
|
||||||
.then((token) => dispatch(actions.loginSuccess(token)))
|
.then((token) => {
|
||||||
|
dispatch(actions.loginSuccess(token))
|
||||||
|
dispatch(connect(socketURL(token)))
|
||||||
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.warn("login error", err);
|
console.warn("login error", err);
|
||||||
throw new RemoteError([
|
throw new RemoteError([
|
||||||
|
@ -193,7 +189,10 @@ export const RemoteService = {
|
||||||
*/
|
*/
|
||||||
logout: () => {
|
logout: () => {
|
||||||
return (dispatch) =>
|
return (dispatch) =>
|
||||||
Endpoint.logout().then(void dispatch(actions.logout()));
|
Endpoint.logout().then(() => {
|
||||||
|
dispatch(disconnect())
|
||||||
|
dispatch(actions.logout())
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -476,71 +475,6 @@ for (const key in RemoteService) {
|
||||||
RemoteService[key] = RemoteService[key].bind(RemoteService);
|
RemoteService[key] = RemoteService[key].bind(RemoteService);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Class to handle connection with the sensor socket */
|
|
||||||
class ServiceSocket {
|
|
||||||
token;
|
|
||||||
authenticated = false;
|
|
||||||
retries = 0;
|
|
||||||
connection;
|
|
||||||
|
|
||||||
static get URL() {
|
|
||||||
const httpURL = new URL(endpointURL());
|
|
||||||
const isSecure = httpURL.protocol === "https:";
|
|
||||||
const protocol = isSecure ? "wss:" : "ws:";
|
|
||||||
const port = httpURL.port || (isSecure ? 443 : 80);
|
|
||||||
return `${protocol}//${httpURL.host}:${port}/sensor-socket`;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new sensor socket connection
|
|
||||||
* @param {string} token - The JWT token (needed for authentication)
|
|
||||||
*/
|
|
||||||
constructor(token) {
|
|
||||||
this.token = token;
|
|
||||||
this.authenticated = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
_init() {
|
|
||||||
this.connection = new WebSocket(ServiceSocket.URL);
|
|
||||||
|
|
||||||
this.connection.onopen = (_) => {
|
|
||||||
this.connection.send(JSON.stringify({ token: this.token }));
|
|
||||||
};
|
|
||||||
|
|
||||||
this.connection.onmessage = (evt) => {
|
|
||||||
let data = JSON.parse(evt.data);
|
|
||||||
|
|
||||||
if (!this.authenticated) {
|
|
||||||
if (data.authenticated) {
|
|
||||||
this.authenticated = true;
|
|
||||||
this.retries = 0;
|
|
||||||
} else {
|
|
||||||
console.error("socket authentication failed", data);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.invokeCallbacks(data);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.connection.onerror = (evt) => {
|
|
||||||
console.warn("socket error", evt);
|
|
||||||
if (this.retries >= 5) {
|
|
||||||
console.error("too many socket connection retries");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.retries++;
|
|
||||||
this._init();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Closes the underlying websocket connection
|
|
||||||
*/
|
|
||||||
close() {
|
|
||||||
this.connection.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Forms {
|
export class Forms {
|
||||||
/**
|
/**
|
||||||
* Attempts to create a new user from the given data.
|
* Attempts to create a new user from the given data.
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import { createStore, applyMiddleware } from "redux";
|
import { createStore, applyMiddleware, compose } from "redux";
|
||||||
import thunk from "redux-thunk";
|
import thunk from "redux-thunk";
|
||||||
import update from "immutability-helper";
|
import update from "immutability-helper";
|
||||||
|
import reduxWebSocket, { connect } from "@giantmachines/redux-websocket";
|
||||||
|
import { socketURL } from "./endpoint";
|
||||||
|
|
||||||
function reducer(previousState, action) {
|
function reducer(previousState, action) {
|
||||||
let newState, change;
|
let newState, change;
|
||||||
|
@ -229,12 +231,20 @@ function reducer(previousState, action) {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
case "REDUX_WEBSOCKET::MESSAGE":
|
||||||
|
const devices = JSON.parse(action.payload.message);
|
||||||
|
console.log(devices);
|
||||||
|
newState = reducer(previousState, {
|
||||||
|
type: "DEVICES_UPDATE",
|
||||||
|
partial: true,
|
||||||
|
devices
|
||||||
|
});
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
console.warn(`Action type ${action.type} unknown`, action);
|
console.warn(`Action type ${action.type} unknown`, action);
|
||||||
return previousState;
|
return previousState;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("new state: ", newState);
|
|
||||||
return newState;
|
return newState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -274,7 +284,14 @@ function createSmartHutStore() {
|
||||||
initialState.login.token = null;
|
initialState.login.token = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return createStore(reducer, initialState, applyMiddleware(thunk));
|
const store = createStore(reducer, initialState,
|
||||||
|
compose(
|
||||||
|
applyMiddleware(thunk),
|
||||||
|
applyMiddleware(reduxWebSocket())));
|
||||||
|
if (initialState.login.loggedIn) {
|
||||||
|
store.dispatch(connect(socketURL(token)));
|
||||||
|
}
|
||||||
|
return store;
|
||||||
}
|
}
|
||||||
|
|
||||||
const smartHutStore = createSmartHutStore();
|
const smartHutStore = createSmartHutStore();
|
||||||
|
|
|
@ -973,6 +973,13 @@
|
||||||
resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.7.5.tgz#77211291c1900a700b8a78cfafda3160d76949ed"
|
resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.7.5.tgz#77211291c1900a700b8a78cfafda3160d76949ed"
|
||||||
integrity sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==
|
integrity sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==
|
||||||
|
|
||||||
|
"@giantmachines/redux-websocket@^1.1.7":
|
||||||
|
version "1.1.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/@giantmachines/redux-websocket/-/redux-websocket-1.1.7.tgz#8c045a359cd3f9a73ef141ce722fd14ae754cd1b"
|
||||||
|
integrity sha512-t90k+NcVInXvppMVpU3c7ZC6i58S/jBPqltckAlKfrtc92YmGZ/He3qYT9OiemlvS+/d+R6P/Ed4yEqKVevYdg==
|
||||||
|
dependencies:
|
||||||
|
redux "~4"
|
||||||
|
|
||||||
"@hapi/address@2.x.x":
|
"@hapi/address@2.x.x":
|
||||||
version "2.1.4"
|
version "2.1.4"
|
||||||
resolved "https://registry.yarnpkg.com/@hapi/address/-/address-2.1.4.tgz#5d67ed43f3fd41a69d4b9ff7b56e7c0d1d0a81e5"
|
resolved "https://registry.yarnpkg.com/@hapi/address/-/address-2.1.4.tgz#5d67ed43f3fd41a69d4b9ff7b56e7c0d1d0a81e5"
|
||||||
|
@ -9008,7 +9015,7 @@ redux-thunk@^2.3.0:
|
||||||
resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.3.0.tgz#51c2c19a185ed5187aaa9a2d08b666d0d6467622"
|
resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.3.0.tgz#51c2c19a185ed5187aaa9a2d08b666d0d6467622"
|
||||||
integrity sha512-km6dclyFnmcvxhAcrQV2AkZmPQjzPDjgVlQtR0EQjxZPyJ0BnMf3in1ryuR8A2qU0HldVRfxYXbFSKlI3N7Slw==
|
integrity sha512-km6dclyFnmcvxhAcrQV2AkZmPQjzPDjgVlQtR0EQjxZPyJ0BnMf3in1ryuR8A2qU0HldVRfxYXbFSKlI3N7Slw==
|
||||||
|
|
||||||
redux@^4.0.5:
|
redux@^4.0.5, redux@~4:
|
||||||
version "4.0.5"
|
version "4.0.5"
|
||||||
resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.5.tgz#4db5de5816e17891de8a80c424232d06f051d93f"
|
resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.5.tgz#4db5de5816e17891de8a80c424232d06f051d93f"
|
||||||
integrity sha512-VSz1uMAH24DM6MF72vcojpYPtrTUu3ByVWfPL1nPfVRb5mZVTve5GnNCUV53QM/BZ66xfWrm0CTWoM+Xlz8V1w==
|
integrity sha512-VSz1uMAH24DM6MF72vcojpYPtrTUu3ByVWfPL1nPfVRb5mZVTve5GnNCUV53QM/BZ66xfWrm0CTWoM+Xlz8V1w==
|
||||||
|
|
Loading…
Reference in a new issue