Skip to content

Commit

Permalink
Feat: websocket for GetMessages (#122)
Browse files Browse the repository at this point in the history
* Feat: user can't retrieve data from Aleph through webSocket

Solution: Add a websocket implementation

* Fix: Replace array mutation by new array in toolshade

* Feat: Add webSocket implementation for Nodejs with tests

* Feat: Add the history parameter
  • Loading branch information
Rgascoin authored Jan 3, 2024
1 parent b86ef88 commit 69198aa
Show file tree
Hide file tree
Showing 10 changed files with 383 additions and 15 deletions.
15 changes: 10 additions & 5 deletions examples/toolshed/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ import { useReducer } from 'react'

import { initState, reducer } from './reducer'

import SelectProvider from './components/SelectProvider'
import KeypairConfig from './components/KeypairConfig'
import WalletConfig from './components/WalletConfig'
import MessageConfig from './components/MessageConfig'
import SelectProvider from './components/SelectProvider';
import KeypairConfig from './components/KeypairConfig';
import WalletConfig from './components/WalletConfig';
import MessageConfig from './components/MessageConfig';
import WebSocket from './components/WebSocket';
import EncryptionConfig from "./components/EncryptionConfig";
import HardwareConfig from "./components/HardwareConfig";
import {ECIESAccount} from "../../../src/accounts/account";
Expand Down Expand Up @@ -63,13 +64,17 @@ function App() {
state.account &&
<MessageConfig state={state} />
}

<section style={{marginTop: '6em', borderStyle: 'solid', borderBottom: '0px', borderColor: 'lightgray', borderLeft: '0px', borderRight: '0px'}}>
<h2>WebSocket</h2>
<WebSocket />
</section>
{
state.account && state.account instanceof ECIESAccount &&
<EncryptionConfig state={state} />
}
</div>


</main>
)
}
Expand Down
66 changes: 66 additions & 0 deletions examples/toolshed/src/components/WebSocket.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { useState } from "react";
import { GetMessagesSocket } from "../../../../src/messages/any";
import {AlephSocket, SocketResponse} from "../../../../src/messages/any/getMessagesSocket";

function WebSocket() {
const [socket, setSocket] = useState<AlephSocket | undefined>()
const [data, setData] = useState<SocketResponse[]>([]);

const startSocket = async () => {
const newSocket = GetMessagesSocket({});

setSocket(newSocket);
}

const displayContent = async () => {
if (!socket) return;
setData([...socket.getData()]);
}

const clearSocket = async () => {
socket?.clearData();
await displayContent();
}

const closeSocket = async () => {
socket?.closeSocket()
setSocket(undefined);
}

return (
<>
<div style={{display: 'flex', flexWrap:'wrap', gap: '6px'}}>
<button onClick={startSocket} disabled={!!socket}>Open WebSocket</button>
<button onClick={displayContent} disabled={!socket}>Display</button>
<button onClick={clearSocket} disabled={!socket}>Clear Data</button>
<button onClick={closeSocket} disabled={!socket}>Close Socket</button>
</div>

<p>Total: {data.length}</p>
<div>
{ !!socket ? (<table style={{marginTop: '2em'}}>
<thead>
<tr>
<th>Sender</th>
<th>Chain</th>
<th>Hash</th>
</tr>
</thead>
<tbody>
{data.map((line, id) => {
return (
<tr key={id}>
<td>{`${line.sender.substring(0, 6,)}...${line.sender.substring(line.sender.length - 4,)}`}</td>
<td>{line.chain}</td>
<td>{line.item_hash}</td>
</tr>
)
})}
</tbody>
</table>) : (<p>Socket is closed</p>)}
</div>
</>
)
}

export default WebSocket
43 changes: 35 additions & 8 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@
"semver": "^7.3.8",
"sha.js": "^2.4.11",
"tweetnacl": "^1.0.3",
"uuid": "^8.3.2"
"uuid": "^8.3.2",
"ws": "^8.12.1"
}
}
1 change: 1 addition & 0 deletions src/global.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export const DEFAULT_API_V2 = "https://api2.aleph.im";
export const DEFAULT_API_WS_V2 = "ws://api2.aleph.im";
66 changes: 66 additions & 0 deletions src/messages/any/AlephNodeWebSocket.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { GetMessagesSocketParams, SocketResponse } from "./getMessagesSocket";
import WebSocket from "ws";

/**
* This class is used to manipulate Node Web Socket to list Aleph Messages
*/
export class AlephNodeWebSocket {
private readonly socket: WebSocket;
private data: SocketResponse[];

private isOpen: boolean;

constructor(queryParam: GetMessagesSocketParams, apiServer: string) {
// eslint-disable-next-line @typescript-eslint/no-this-alias
const self = this;
this.isOpen = false;
this.data = [];
let queryParamString = "";

Object.entries(queryParam).forEach(([key, value]) => {
if (!!value) queryParamString = queryParamString + `&${key}=${value}`;
});
if (!!queryParamString) queryParamString = queryParamString.substring(1);
this.socket = new WebSocket(`${apiServer}/api/ws0/messages?${queryParamString}`);

// ON OPEN SOCKET
// eslint-disable-next-line @typescript-eslint/no-unused-vars
this.socket.on("open", function open() {
console.log("[Aleph-NodeWebSocket] Connection established");
self.isOpen = true;
});

// ON RECEIVE DATA
this.socket.on("message", function message(data) {
self.data.push(JSON.parse(data.toString()));
});

// ON CLOSE SOCKET
this.socket.on("close", function close() {
console.log(`[Aleph-NodeWebSocket] Connection closed`);
self.isOpen = false;
});

// ON ERROR
this.socket.on("error", console.error);
}

public getData = (): SocketResponse[] => {
return this.data;
};
public getSocket = (): WebSocket => {
return this.socket;
};

public getIsOpen = (): boolean => {
return this.isOpen;
};

public clearData = (): void => {
this.data = [];
};

public closeSocket = (): void => {
this.socket.close(1000, "Work complete");
};
}
69 changes: 69 additions & 0 deletions src/messages/any/AlephWebSocket.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { GetMessagesSocketParams, SocketResponse } from "./getMessagesSocket";

/**
* This class is used to manipulate Web Socket to list Aleph Messages
*/
export class AlephWebSocket {
private readonly socket: WebSocket;
private data: SocketResponse[];

private isOpen: boolean;

constructor(queryParam: GetMessagesSocketParams, apiServer: string) {
// eslint-disable-next-line @typescript-eslint/no-this-alias
const self = this;
this.isOpen = false;
this.data = [];
let queryParamString = "";

Object.entries(queryParam).forEach(([key, value]) => {
if (!!value) queryParamString = queryParamString + `&${key}=${value}`;
});
if (!!queryParamString) queryParamString = queryParamString.substring(1);
this.socket = new WebSocket(`${apiServer}/api/ws0/messages?${queryParamString}`);

// ON OPEN SOCKET
// eslint-disable-next-line @typescript-eslint/no-unused-vars
this.socket.onopen = function (_) {
console.log("[Aleph-webSocket] Connection established");
self.isOpen = true;
};

// ON RECEIVE DATA
this.socket.onmessage = function (event) {
self.data.push(JSON.parse(event.data));
};

// ON CLOSE SOCKET
this.socket.onclose = function (event) {
self.isOpen = false;
if (event.wasClean)
console.log(`[Aleph-webSocket] Connection closed cleanly, code=${event.code} reason=${event.reason}`);
else console.log("[Aleph-webSocket] Connection died");
};

// ON ERROR
this.socket.onerror = function (error) {
console.log("[Aleph-webSocket]: error: ", error);
};
}

public getData = (): SocketResponse[] => {
return this.data;
};
public getSocket = (): WebSocket => {
return this.socket;
};

public getIsOpen = (): boolean => {
return this.isOpen;
};

public clearData = (): void => {
this.data = [];
};

public closeSocket = (): void => {
this.socket.close(1000, "Work complete");
};
}
Loading

0 comments on commit 69198aa

Please sign in to comment.