diff --git a/package-lock.json b/package-lock.json index 629884b..743905a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1552,14 +1552,28 @@ "@pushrocks/smartpromise": "^3.0.6", "@types/through2": "^2.0.34", "through2": "^3.0.1" + }, + "dependencies": { + "@pushrocks/smartjson": { + "version": "3.0.10", + "resolved": "https://verdaccio.lossless.one/@pushrocks%2fsmartjson/-/smartjson-3.0.10.tgz", + "integrity": "sha512-0tBkET2yjmSSIf4DlgeyU8U/J2EshTmQGuMY28EjPq9VvuCFXLh72WmETpA4QqKRMqhWp1+P+RZgnQupW3GQxQ==", + "requires": { + "@types/fast-json-stable-stringify": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "lodash.clonedeep": "^4.5.0" + } + } } }, "@pushrocks/smartjson": { - "version": "3.0.10", - "resolved": "https://verdaccio.lossless.one/@pushrocks%2fsmartjson/-/smartjson-3.0.10.tgz", - "integrity": "sha512-0tBkET2yjmSSIf4DlgeyU8U/J2EshTmQGuMY28EjPq9VvuCFXLh72WmETpA4QqKRMqhWp1+P+RZgnQupW3GQxQ==", + "version": "4.0.5", + "resolved": "https://verdaccio.lossless.one/@pushrocks%2fsmartjson/-/smartjson-4.0.5.tgz", + "integrity": "sha512-i4kBjZSbs1t8swcAPEdPkDJHci2higzvMIkNUKTgXWrcxUFMuOis/B5huUnnIqg/Td8R+mAdf/B/CMfgjABTlg==", "requires": { + "@types/buffer-json": "^2.0.0", "@types/fast-json-stable-stringify": "^2.0.0", + "buffer-json": "^2.0.0", "fast-json-stable-stringify": "^2.1.0", "lodash.clonedeep": "^4.5.0" } @@ -2014,14 +2028,14 @@ } }, "@pushrocks/tapbundle": { - "version": "3.2.9", - "resolved": "https://verdaccio.lossless.one/@pushrocks%2ftapbundle/-/tapbundle-3.2.9.tgz", - "integrity": "sha512-vtmYL/l7BZvAzySh7cYnnTG6CFMp5zYtowJuMAmqUjhIaQaWW1Tvbrpjp7lVwRXj2JlL/i69KcJ6RVdLItK+rA==", + "version": "3.2.10", + "resolved": "https://verdaccio.lossless.one/@pushrocks%2ftapbundle/-/tapbundle-3.2.10.tgz", + "integrity": "sha512-EPFSiFMx6uxWcZgANT1yDigTknVl1FCRIaiuf2AEkpg4vR5ZIZQKDIWkrFZY0ajUWJ6vCM631TmIcJvUnyWJXw==", "requires": { - "@pushrocks/smartdelay": "^2.0.9", - "@pushrocks/smartenv": "^4.0.10", - "@pushrocks/smartpromise": "^3.0.2", - "@pushrocks/smarttime": "^3.0.19", + "@pushrocks/smartdelay": "^2.0.10", + "@pushrocks/smartenv": "^4.0.16", + "@pushrocks/smartpromise": "^3.1.3", + "@pushrocks/smarttime": "^3.0.38", "smartchai": "^2.0.1" } }, @@ -2220,9 +2234,9 @@ "dev": true }, "@types/engine.io": { - "version": "3.1.4", - "resolved": "https://verdaccio.lossless.one/@types%2fengine.io/-/engine.io-3.1.4.tgz", - "integrity": "sha512-98rXVukLD6/ozrQ2O80NAlWDGA4INg+tqsEReWJldqyi2fulC9V7Use/n28SWgROXKm6003ycWV4gZHoF8GA6w==", + "version": "3.1.5", + "resolved": "https://verdaccio.lossless.one/@types%2fengine.io/-/engine.io-3.1.5.tgz", + "integrity": "sha512-DLVpLEGTEZGBXOYoYoagHSxXkDHONc0fZouF2ayw7Q18aRu1Afwci+1CFKvPpouCUOVWP+dmCaAWpQjswe7kpg==", "requires": { "@types/node": "*" } @@ -2402,9 +2416,9 @@ "dev": true }, "@types/socket.io": { - "version": "2.1.12", - "resolved": "https://verdaccio.lossless.one/@types%2fsocket.io/-/socket.io-2.1.12.tgz", - "integrity": "sha512-oStc5VFkpb0AsjOxQUj9ztX5Iziatyla/rjZTYbFGoVrrKwd+JU2mtxk7iSl5RGYx9WunLo6UXW1fBzQok/ZyA==", + "version": "2.1.13", + "resolved": "https://verdaccio.lossless.one/@types%2fsocket.io/-/socket.io-2.1.13.tgz", + "integrity": "sha512-JRgH3nCgsWel4OPANkhH8TelpXvacAJ9VeryjuqCDiaVDMpLysd6sbt0dr6Z15pqH3p2YpOT3T1C5vQ+O/7uyg==", "requires": { "@types/engine.io": "*", "@types/node": "*", @@ -8083,9 +8097,9 @@ "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" }, "pathval": { - "version": "1.1.0", - "resolved": "https://verdaccio.lossless.one/pathval/-/pathval-1.1.0.tgz", - "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=" + "version": "1.1.1", + "resolved": "https://verdaccio.lossless.one/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==" }, "pbkdf2": { "version": "3.1.1", diff --git a/package.json b/package.json index b28c086..1a98902 100644 --- a/package.json +++ b/package.json @@ -26,11 +26,12 @@ "@pushrocks/smartdelay": "^2.0.10", "@pushrocks/smartenv": "^4.0.16", "@pushrocks/smartexpress": "^3.0.100", + "@pushrocks/smartjson": "^4.0.5", "@pushrocks/smartlog": "^2.0.39", "@pushrocks/smartpromise": "^3.1.3", "@pushrocks/smartrx": "^2.0.19", "@pushrocks/smarttime": "^3.0.38", - "@types/socket.io": "^2.1.12", + "@types/socket.io": "^2.1.13", "@types/socket.io-client": "^1.4.35", "socket.io": "^3.1.0", "socket.io-client": "^3.1.0" @@ -39,7 +40,7 @@ "@gitzone/tsbuild": "^2.1.25", "@gitzone/tsrun": "^1.2.12", "@gitzone/tstest": "^1.0.52", - "@pushrocks/tapbundle": "^3.2.9", + "@pushrocks/tapbundle": "^3.2.10", "@types/node": "^14.14.22", "tslint": "^6.1.3", "tslint-config-prettier": "^1.18.0" diff --git a/test/test.ts b/test/test.ts index fee755c..87579ec 100644 --- a/test/test.ts +++ b/test/test.ts @@ -90,6 +90,17 @@ tap.test('should react to a new websocket connection from client', async () => { await testSmartsocketClient.connect(); }); +tap.test('should be able to tag a connection', async (tools) => { + await testSmartsocketClient.addTag({ + id: 'awesome', + payload: 'yes' + }); + const tagOnServerSide = await testSmartsocket.socketConnections.find((socketConnection) => { + return true; + }).getTagById('awesome'); + expect(tagOnServerSide.payload).to.equal('yes'); +}) + tap.test('2 clients should connect in parallel', async () => { // TODO: implement parallel test }); diff --git a/ts/interfaces/index.ts b/ts/interfaces/index.ts index 0fb3261..d1a5ef5 100644 --- a/ts/interfaces/index.ts +++ b/ts/interfaces/index.ts @@ -1 +1,2 @@ export * from './connection'; +export * from './tag'; diff --git a/ts/interfaces/tag.ts b/ts/interfaces/tag.ts new file mode 100644 index 0000000..ccde7c3 --- /dev/null +++ b/ts/interfaces/tag.ts @@ -0,0 +1,6 @@ +export interface ITag { + id: string; + payload: T; +} + +export type TTagStore = {[key: string]: ITag}; \ No newline at end of file diff --git a/ts/smartsocket.classes.smartsocketclient.ts b/ts/smartsocket.classes.smartsocketclient.ts index 7e45a49..6346503 100644 --- a/ts/smartsocket.classes.smartsocketclient.ts +++ b/ts/smartsocket.classes.smartsocketclient.ts @@ -45,6 +45,40 @@ export class SmartsocketClient { public socketRequests = new plugins.lik.ObjectMap>(); public socketRoles = new plugins.lik.ObjectMap(); + // tagStore + private tagStore: {[key: string]: interfaces.ITag} = {}; + private tagStoreSubscription: plugins.smartrx.rxjs.Subscription; + + /** + * adds a tag to a connection + */ + public async addTag(tagArg: interfaces.ITag) { + if (this.socketConnection) { + await this.socketConnection.addTag(tagArg); + } else { + this.tagStore[tagArg.id] = tagArg; + } + } + + /** + * gets a tag by id + * @param tagIdArg + */ + public async getTagById(tagIdArg: interfaces.ITag['id']) { + return this.tagStore[tagIdArg]; + }; + + /** + * removes a tag from a connection + */ + public async removeTagById(tagIdArg: interfaces.ITag['id']) { + if (this.socketConnection) { + this.socketConnection.removeTagById(tagIdArg); + } else { + delete this.tagStore[tagIdArg]; + } + } + constructor(optionsArg: ISmartsocketClientOptions) { this.alias = optionsArg.alias; this.serverUrl = optionsArg.url; @@ -129,6 +163,13 @@ export class SmartsocketClient { // handle connection this.socketConnection.socket.on('connect', async () => { + this.tagStoreSubscription?.unsubscribe(); + this.tagStoreSubscription = this.socketConnection.tagStoreObservable.subscribe(tagStoreArg => { + this.tagStore = tagStoreArg + }); + for (const keyArg of Object.keys(this.tagStore)) { + this.socketConnection.addTag(this.tagStore[keyArg]); + } this.updateStatus('connected'); }); diff --git a/ts/smartsocket.classes.socketconnection.ts b/ts/smartsocket.classes.socketconnection.ts index 14bdfbc..deb7040 100644 --- a/ts/smartsocket.classes.socketconnection.ts +++ b/ts/smartsocket.classes.socketconnection.ts @@ -57,6 +57,10 @@ export class SocketConnection { public eventSubject = new plugins.smartrx.rxjs.Subject(); public eventStatus: interfaces.TConnectionStatus = 'new'; + private tagStore: interfaces.TTagStore = {}; + public tagStoreObservable = new plugins.smartrx.rxjs.Subject(); + public remoteTagStoreObservable = new plugins.smartrx.rxjs.Subject(); + constructor(optionsArg: ISocketConnectionConstructorOptions) { this.alias = optionsArg.alias; this.authenticated = optionsArg.authenticated; @@ -82,6 +86,42 @@ export class SocketConnection { }); } + /** + * adds a tag to a connection + */ + public async addTag(tagArg: interfaces.ITag) { + const done = plugins.smartpromise.defer(); + this.tagStore[tagArg.id] = tagArg; + this.tagStoreObservable.next(this.tagStore); + const remoteSubscription = this.remoteTagStoreObservable.subscribe((remoteTagStore) => { + const localTagString = plugins.smartjson.stringify(tagArg); + const remoteTagString = plugins.smartjson.stringify(remoteTagStore[tagArg.id]) + if (localTagString === remoteTagString) { + remoteSubscription.unsubscribe(); + done.resolve(); + } + }) + this.socket.emit('updateTagStore', this.tagStore); + await done.promise; + } + + /** + * gets a tag by id + * @param tagIdArg + */ + public async getTagById(tagIdArg: interfaces.ITag['id']) { + return this.tagStore[tagIdArg]; + }; + + /** + * removes a tag from a connection + */ + public async removeTagById(tagIdArg: interfaces.ITag['id']) { + delete this.tagStore[tagIdArg]; + this.tagStoreObservable.next(this.tagStore); + this.socket.emit('updateTagStore', this.tagStore); + } + // authenticating -------------------------- /** @@ -91,7 +131,7 @@ export class SocketConnection { const done = plugins.smartpromise.defer(); this.socket.on('dataAuth', async (dataArg: ISocketConnectionAuthenticationObject) => { logger.log('info', 'received authentication data. now hashing and comparing...'); - this.socket.removeListener('dataAuth', () => {}); + this.socket.removeAllListeners('dataAuth'); if (await SocketRole.checkPasswordForRole(dataArg, this.smartsocketRef)) { // TODO: authenticate password this.alias = dataArg.alias; @@ -150,6 +190,20 @@ export class SocketConnection { ); targetSocketRequest.handleResponse(dataArg); }); + + this.socket.on('updateTagStore', async (tagStoreArg: interfaces.TTagStore) => { + const exitingStoreString = plugins.smartjson.stringify(this.tagStore); + const newStoreString = plugins.smartjson.stringify(tagStoreArg); + console.log(exitingStoreString); + console.log(newStoreString); + if (exitingStoreString !== newStoreString) { + this.tagStore = tagStoreArg; + this.socket.emit('updateTagStore', this.tagStore); + this.tagStoreObservable.next(this.tagStore); + } + this.remoteTagStoreObservable.next(tagStoreArg); + }) + logger.log('info', `now listening to function requests for ${this.alias}`); done.resolve(this); } else { diff --git a/ts/smartsocket.plugins.ts b/ts/smartsocket.plugins.ts index d829717..8fc1e92 100644 --- a/ts/smartsocket.plugins.ts +++ b/ts/smartsocket.plugins.ts @@ -8,6 +8,7 @@ import * as isohash from '@pushrocks/isohash'; import * as isounique from '@pushrocks/isounique'; import * as lik from '@pushrocks/lik'; import * as smartenv from '@pushrocks/smartenv'; +import * as smartjson from '@pushrocks/smartjson'; import * as smartlog from '@pushrocks/smartlog'; import * as smartdelay from '@pushrocks/smartdelay'; import * as smartpromise from '@pushrocks/smartpromise'; @@ -19,6 +20,7 @@ export { isounique, lik, smartenv, + smartjson, smartlog, smartdelay, smartpromise,