summaryrefslogtreecommitdiff
path: root/client
diff options
context:
space:
mode:
Diffstat (limited to 'client')
-rw-r--r--client/jest.config.js3
-rw-r--r--client/package.json1
-rw-r--r--client/src/config.model.ts2
-rw-r--r--client/src/database-connection.model.ts3
-rw-r--r--client/src/database-connection.ts120
-rw-r--r--client/src/index.ts1
-rw-r--r--client/tsconfig.json4
-rw-r--r--client/yarn.lock8
8 files changed, 121 insertions, 21 deletions
diff --git a/client/jest.config.js b/client/jest.config.js
index 758fa13..6f79391 100644
--- a/client/jest.config.js
+++ b/client/jest.config.js
@@ -1,4 +1,5 @@
module.exports = {
preset: 'ts-jest',
- testEnvironment: 'node'
+ testEnvironment: 'node',
+ modulePathIgnorePatterns: ['dist']
}
diff --git a/client/package.json b/client/package.json
index b7489f0..66ea261 100644
--- a/client/package.json
+++ b/client/package.json
@@ -13,6 +13,7 @@
"devDependencies": {
"@types/jest": "^23.3.13",
"@types/node": "^10.12.18",
+ "@types/ws": "^6.0.1",
"jest": "^23.6.0",
"ts-jest": "^23.10.5",
"ts-node": "^7.0.1",
diff --git a/client/src/config.model.ts b/client/src/config.model.ts
index 278f7f7..09925bf 100644
--- a/client/src/config.model.ts
+++ b/client/src/config.model.ts
@@ -1,4 +1,4 @@
export interface Config {
- wsPort: number
+ wsUrl: string
httpUrl: string
}
diff --git a/client/src/database-connection.model.ts b/client/src/database-connection.model.ts
index 0523e22..86999be 100644
--- a/client/src/database-connection.model.ts
+++ b/client/src/database-connection.model.ts
@@ -1,7 +1,8 @@
export interface DatabaseConnection {
init(): Promise<any>
- close(): Promise<any>
+ close(): any
subscribe(path: string, callback: (e: any) => any): Promise<() => any>
write(path: string, toWrite: Object): Promise<any>
+ read(path: string): Promise<any>
remove(path: string): Promise<any>
}
diff --git a/client/src/database-connection.ts b/client/src/database-connection.ts
index 3cf733c..f37b298 100644
--- a/client/src/database-connection.ts
+++ b/client/src/database-connection.ts
@@ -1,7 +1,28 @@
import { Config } from './config.model'
+import { getKey } from '../../lib/util'
+import { encodePath } from '../../lib/path'
import { DatabaseConnection } from './database-connection.model'
+import { DatabaseChange } from '../../lib/database-change.model'
+import { Body } from 'node-fetch'
-const IS_NODE = true
+import WebSocket from 'ws'
+
+const objectToQuery = (query: Object): string =>
+ Object.values(query).reduce(
+ (acc: string, [key, val]: [string, string]) => acc + `${key}=${val}&`,
+ '?'
+ )
+
+// each path has one or more callbacks
+// that must be fired with each change
+interface CallbackTable {
+ [path: string]: CallbackRef[]
+}
+
+interface CallbackRef {
+ id: string
+ callback: (e: Object) => void
+}
/**
* Client side implementation is implemented using
@@ -10,31 +31,100 @@ const IS_NODE = true
*/
export const dbFactory = (config: Config): DatabaseConnection => {
// TODO figure out how to patch
- // in native fetch instead
+ // in native fetch/websocke instead
// when this is running in the browser
- const send = require('node-fetch')
+ const nodeFetch = require('node-fetch')
+ const WebSocket = require('ws')
+
+ let ws: WebSocket | null
+ let subCallbacks: CallbackTable = {}
+
+ const send = (method: string) => (
+ route: string,
+ body: Object | void
+ ): Promise<Body> => {
+ const isGet = method === 'GET'
+ if (isGet && body) {
+ route += objectToQuery(body)
+ }
+ return nodeFetch(route, {
+ method,
+ ...(isGet ? undefined : { body: JSON.stringify(body) }),
+ headers: {
+ Accept: 'application/json',
+ 'Content-Type': 'application/json'
+ }
+ })
+ }
+
+ const hget = (route: string, body: Object | void) =>
+ send('GET')(route, body).then((r: any) => r.json())
+ const hpost = send('POST')
+
const write = (path: string, toWrite: Object | null) => {
const { httpUrl } = config
- const body = JSON.stringify({
+ return hpost(`${httpUrl}/write`, {
path,
toWrite
})
- return send(`${httpUrl}/write`, {
- method: 'POST',
- body,
- headers: {
- Accept: 'application/json',
- 'Content-Type': 'application/json'
+ }
+
+ const read = (path: string): Promise<any> => {
+ const { httpUrl } = config
+ return hget(`${httpUrl}/read/${encodePath(path)}`)
+ }
+
+ const socketMessageHandler = (event: {
+ data: any
+ type: string
+ target: WebSocket
+ }) => {
+ const dbChange: DatabaseChange = JSON.parse(event.data)
+ const callbackRefs = subCallbacks[dbChange.path]
+ if (callbackRefs && callbackRefs.length) {
+ for (let ref of callbackRefs) {
+ ref.callback(dbChange.change)
}
- })
+ }
}
+
return {
- async init() {},
- async close() {},
- async subscribe(path: string, callback: (e: any) => Promise<any>) {
- return () => {}
+ init() {
+ return new Promise(resolve => {
+ ws = new WebSocket(config.wsUrl)
+ // @ts-ignore
+ ws.addEventListener('message', socketMessageHandler)
+ // @ts-ignore
+ ws.addEventListener('open', () => resolve())
+ })
+ },
+ close() {
+ if (ws) {
+ ws.removeEventListener('message', socketMessageHandler)
+ ws.close()
+ ws = null
+ }
+ },
+ async subscribe(path: string, callback: (e: Object) => void) {
+ const id = getKey('callbackref')
+ const ref: CallbackRef = {
+ id,
+ callback
+ }
+ if (!subCallbacks[path]) {
+ const { httpUrl } = config
+ subCallbacks[path] = [ref]
+ await hpost(`${httpUrl}/subscribe`, { path })
+ } else subCallbacks[path].push(ref)
+ // return a fucntion
+ // to remove our new sub from
+ // the table
+ return () => {
+ subCallbacks[path] = subCallbacks[path].filter(k => k.id !== id)
+ }
},
write,
+ read,
async remove(path: string) {
write(path, null)
}
diff --git a/client/src/index.ts b/client/src/index.ts
index f11e59c..d2a9d21 100644
--- a/client/src/index.ts
+++ b/client/src/index.ts
@@ -1,4 +1,3 @@
export { dbFactory } from './database-connection'
export { DatabaseConnection } from './database-connection.model'
export { Config } from './config.model'
-declare var fetch: any
diff --git a/client/tsconfig.json b/client/tsconfig.json
index 1b31a6d..abe6846 100644
--- a/client/tsconfig.json
+++ b/client/tsconfig.json
@@ -34,8 +34,8 @@
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
/* Additional Checks */
- // "noUnusedLocals": true, /* Report errors on unused locals. */
- // "noUnusedParameters": true, /* Report errors on unused parameters. */
+ "noUnusedLocals": true /* Report errors on unused locals. */,
+ "noUnusedParameters": true /* Report errors on unused parameters. */,
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
diff --git a/client/yarn.lock b/client/yarn.lock
index 97a7de5..b29c5e1 100644
--- a/client/yarn.lock
+++ b/client/yarn.lock
@@ -89,6 +89,14 @@
"@types/glob" "*"
"@types/node" "*"
+"@types/ws@^6.0.1":
+ version "6.0.1"
+ resolved "https://registry.yarnpkg.com/@types/ws/-/ws-6.0.1.tgz#ca7a3f3756aa12f62a0a62145ed14c6db25d5a28"
+ integrity sha512-EzH8k1gyZ4xih/MaZTXwT2xOkPiIMSrhQ9b8wrlX88L0T02eYsddatQlwVFlEPyEqV0ChpdpNnE51QPH6NVT4Q==
+ dependencies:
+ "@types/events" "*"
+ "@types/node" "*"
+
abab@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.0.tgz#aba0ab4c5eee2d4c79d3487d85450fb2376ebb0f"