From 0805d9d4d0d0209b0c7c4ec88a7451bc20a80daf Mon Sep 17 00:00:00 2001 From: "Claudio Maggioni (maggicl)" Date: Sun, 12 Apr 2020 17:46:16 +0200 Subject: [PATCH] Redux websockets implemented --- smart-hut/package.json | 1 + smart-hut/src/endpoint.js | 20 +++++++++ smart-hut/src/remote.js | 88 +++++---------------------------------- smart-hut/src/store.js | 25 +++++++++-- smart-hut/yarn.lock | 9 +++- 5 files changed, 61 insertions(+), 82 deletions(-) create mode 100644 smart-hut/src/endpoint.js diff --git a/smart-hut/package.json b/smart-hut/package.json index 236574a..a73a712 100644 --- a/smart-hut/package.json +++ b/smart-hut/package.json @@ -3,6 +3,7 @@ "version": "0.1.0", "private": true, "dependencies": { + "@giantmachines/redux-websocket": "^1.1.7", "@material-ui/core": "^4.9.4", "@material-ui/icons": "^4.9.1", "@testing-library/jest-dom": "^4.2.4", diff --git a/smart-hut/src/endpoint.js b/smart-hut/src/endpoint.js new file mode 100644 index 0000000..4891f1e --- /dev/null +++ b/smart-hut/src/endpoint.js @@ -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; +} + \ No newline at end of file diff --git a/smart-hut/src/remote.js b/smart-hut/src/remote.js index ecd3074..c4814e9 100644 --- a/smart-hut/src/remote.js +++ b/smart-hut/src/remote.js @@ -1,6 +1,9 @@ import smartHutStore from "./store"; import actions from "./storeActions"; import axios from "axios"; +import { endpointURL, socketURL } from "./endpoint"; +import { connect, disconnect } from "@giantmachines/redux-websocket"; + /** * 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 = { axiosInstance: axios.create({ baseURL: endpointURL(), @@ -176,7 +169,10 @@ export const RemoteService = { login: (usernameOrEmail, password) => { return (dispatch) => { return Endpoint.login(usernameOrEmail, password) - .then((token) => dispatch(actions.loginSuccess(token))) + .then((token) => { + dispatch(actions.loginSuccess(token)) + dispatch(connect(socketURL(token))) + }) .catch((err) => { console.warn("login error", err); throw new RemoteError([ @@ -193,7 +189,10 @@ export const RemoteService = { */ logout: () => { 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); } -/** 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 { /** * Attempts to create a new user from the given data. diff --git a/smart-hut/src/store.js b/smart-hut/src/store.js index 1713b08..2aa78fe 100644 --- a/smart-hut/src/store.js +++ b/smart-hut/src/store.js @@ -1,6 +1,8 @@ -import { createStore, applyMiddleware } from "redux"; +import { createStore, applyMiddleware, compose } from "redux"; import thunk from "redux-thunk"; import update from "immutability-helper"; +import reduxWebSocket, { connect } from "@giantmachines/redux-websocket"; +import { socketURL } from "./endpoint"; function reducer(previousState, action) { let newState, change; @@ -229,12 +231,20 @@ function reducer(previousState, action) { }, }); 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: console.warn(`Action type ${action.type} unknown`, action); return previousState; } - - console.log("new state: ", newState); + return newState; } @@ -274,7 +284,14 @@ function createSmartHutStore() { 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(); diff --git a/smart-hut/yarn.lock b/smart-hut/yarn.lock index 7501bf0..4ade4c1 100644 --- a/smart-hut/yarn.lock +++ b/smart-hut/yarn.lock @@ -973,6 +973,13 @@ resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.7.5.tgz#77211291c1900a700b8a78cfafda3160d76949ed" 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": version "2.1.4" 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" integrity sha512-km6dclyFnmcvxhAcrQV2AkZmPQjzPDjgVlQtR0EQjxZPyJ0BnMf3in1ryuR8A2qU0HldVRfxYXbFSKlI3N7Slw== -redux@^4.0.5: +redux@^4.0.5, redux@~4: version "4.0.5" resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.5.tgz#4db5de5816e17891de8a80c424232d06f051d93f" integrity sha512-VSz1uMAH24DM6MF72vcojpYPtrTUu3ByVWfPL1nPfVRb5mZVTve5GnNCUV53QM/BZ66xfWrm0CTWoM+Xlz8V1w==