diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 63b309d..938529d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,5 +1,5 @@ # gitzone ci_default -image: hosttoday/ht-docker-node:npmci +image: registry.gitlab.com/hosttoday/ht-docker-node:npmci cache: paths: @@ -49,14 +49,14 @@ testLTS: tags: - docker - notpriv - -testSTABLE: + +testBuild: stage: test script: - npmci npm prepare - - npmci node install stable + - npmci node install lts - npmci npm install - - npmci npm test + - npmci command npm run build coverage: /\d+.?\d+?\%\s*coverage/ tags: - docker @@ -65,7 +65,7 @@ testSTABLE: release: stage: release script: - - npmci node install stable + - npmci node install lts - npmci npm publish only: - tags @@ -98,7 +98,9 @@ trigger: - notpriv pages: - image: hosttoday/ht-docker-node:npmci + image: hosttoday/ht-docker-dbase:npmci + services: + - docker:18-dind stage: metadata script: - npmci command npm install -g @gitzone/tsdoc diff --git a/package.json b/package.json index 9ad4bf7..1a62fb6 100644 --- a/package.json +++ b/package.json @@ -42,9 +42,10 @@ "ts_web/*", "dist/*", "dist_web/*", + "dist_ts_web/*", "assets/*", "cli.js", "npmextra.json", "readme.md" ] -} +} \ No newline at end of file diff --git a/ts/interfaces/universeactions.interfaces.ts b/ts/interfaces/universeactions.interfaces.ts index 94a581c..243057a 100644 --- a/ts/interfaces/universeactions.interfaces.ts +++ b/ts/interfaces/universeactions.interfaces.ts @@ -1,4 +1,8 @@ -export type IServerCallActions = 'subscribe' | 'sendmessage' | 'unsubscribe'; +export type IServerCallActions = + | 'channelSubscription' + | 'processMessage' + | 'channelUnsubscribe' + | 'terminateConnection'; /** * the interface for a subscription diff --git a/ts/interfaces/universeconnection.interfaces.ts b/ts/interfaces/universeconnection.interfaces.ts index 7f239ef..7ad4d2f 100644 --- a/ts/interfaces/universeconnection.interfaces.ts +++ b/ts/interfaces/universeconnection.interfaces.ts @@ -1,4 +1,4 @@ export interface IAuthenticationRequest { channelName: string; password: string; -} \ No newline at end of file +} diff --git a/ts/smartuniverse.classes.clientuniverse.ts b/ts/smartuniverse.classes.clientuniverse.ts index 442dc4e..2e40984 100644 --- a/ts/smartuniverse.classes.clientuniverse.ts +++ b/ts/smartuniverse.classes.clientuniverse.ts @@ -74,13 +74,13 @@ export class ClientUniverse { */ public async sendMessage(messageArg: interfaces.IMessageCreator) { await this.checkConnection(); - const requestBody: interfaces.IUniverseMessage = { + const universeMessageToSend: interfaces.IUniverseMessage = { id: plugins.smartunique.shortId(), timestamp: Date.now(), passphrase: (await this.getChannel(messageArg.targetChannelName)).passphrase, ...messageArg }; - // TODO: User websocket connection if available + await this.smartsocketClient.serverCall('processMessage', universeMessageToSend); } public close() { @@ -113,13 +113,21 @@ export class ClientUniverse { const unsubscribe = new plugins.smartsocket.SocketFunction({ funcName: 'unsubscribe', allowedRoles: [], - funcDef: async (data: interfaces.IServerUnsubscribeActionPayload) => {} + funcDef: async (data: interfaces.IServerUnsubscribeActionPayload) => { + throw new Error('TODO'); + } }); /** * should handle a message reception */ - const receiveMessage = async () => {}; + const processMessageSocketFunction = new plugins.smartsocket.SocketFunction({ + funcName: 'processMessage', + allowedRoles: [], + funcDef: async (data: interfaces.IServerUnsubscribeActionPayload) => { + throw new Error('TODO'); + } + }); await this.smartsocketClient.connect(); } diff --git a/ts/smartuniverse.classes.clientuniversechannel.ts b/ts/smartuniverse.classes.clientuniversechannel.ts index 06b28ae..938994e 100644 --- a/ts/smartuniverse.classes.clientuniversechannel.ts +++ b/ts/smartuniverse.classes.clientuniversechannel.ts @@ -50,7 +50,7 @@ export class ClientUniverseChannel implements interfaces.IUniverseChannel { * tells the universe about this instances interest into a channel */ public async subscribe() { - const serverCallActionName: interfaces.IServerCallActions = 'subscribe'; + const serverCallActionName: interfaces.IServerCallActions = 'channelSubscription'; const serverCallActionPayload: interfaces.IServerCallSubscribeActionPayload = { name: this.name, passphrase: this.passphrase diff --git a/ts/smartuniverse.classes.universe.ts b/ts/smartuniverse.classes.universe.ts index d752a67..535220e 100644 --- a/ts/smartuniverse.classes.universe.ts +++ b/ts/smartuniverse.classes.universe.ts @@ -6,7 +6,6 @@ import { UniverseCache, UniverseChannel, UniverseMessage } from './'; import * as paths from './smartuniverse.paths'; import * as interfaces from './interfaces'; -import { UniverseConnectionManager } from './smartuniverse.classes.universeconnectionmanager'; import { UniverseConnection } from './smartuniverse.classes.universeconnection'; export interface ISmartUniverseConstructorOptions { @@ -14,12 +13,11 @@ export interface ISmartUniverseConstructorOptions { } /** - * main class that setsup a Universe + * main class that setups a Universe */ export class Universe { // subinstances public universeCache: UniverseCache; - public universeConnectionManager: UniverseConnectionManager; // options private options: ISmartUniverseConstructorOptions; @@ -37,7 +35,6 @@ export class Universe { constructor(optionsArg: ISmartUniverseConstructorOptions) { this.options = optionsArg; this.universeCache = new UniverseCache(this.options.messageExpiryInMilliseconds); - this.universeConnectionManager = new UniverseConnectionManager(); } /** @@ -95,15 +92,53 @@ export class Universe { const SubscriptionSocketFunction = new plugins.smartsocket.SocketFunction({ allowedRoles: [ClientRole], // there is only one client role, Authentication happens on another level funcName: 'channelSubscription', - funcDef: async (dataArg: interfaces.IServerCallSubscribeActionPayload, socketConnectionArg) => { + funcDef: async ( + dataArg: interfaces.IServerCallSubscribeActionPayload, + socketConnectionArg + ) => { // run in "this context" of this class - (() => { + await (async () => { // TODO: properly add the connection const universeConnection = new UniverseConnection({ socketConnection: socketConnectionArg, authenticationRequests: [] - }) - this.universeConnectionManager.addConnection(universeConnection); + }); + await UniverseConnection.addConnectionToCache(this.universeCache, universeConnection); + return { + 'subscription status': 'success' + }; + })(); + } + }); + + const ProcessMessageSocketFunction = new plugins.smartsocket.SocketFunction({ + allowedRoles: [ClientRole], // there is only one client role, Authentication happens on another level + funcName: 'processMessage', + funcDef: async (dataArg: interfaces.IUniverseMessage, socketConnectionArg) => { + // run in "this" context of this class + await (async () => { + const universeConnection = UniverseConnection.findUniverseConnectionBySocketConnection( + this.universeCache, + socketConnectionArg + ); + if (universeConnection) { + console.log('found UniverseConnection for socket'); + } else { + console.log('universe client not yet present'); + console.log('creating one now as send only'); + const universeConnectionInstance = new UniverseConnection({ + socketConnection: socketConnectionArg, + authenticationRequests: [] + }); + await UniverseConnection.addConnectionToCache( + this.universeCache, + universeConnectionInstance + ); + } + await UniverseChannel.authorizeAMessageForAChannel( + this.universeCache, + UniverseMessage.createMessageFromPayload(dataArg) + ); })(); } }); diff --git a/ts/smartuniverse.classes.universecache.ts b/ts/smartuniverse.classes.universecache.ts index f8a0053..2666374 100644 --- a/ts/smartuniverse.classes.universecache.ts +++ b/ts/smartuniverse.classes.universecache.ts @@ -8,6 +8,7 @@ import { Objectmap } from '@pushrocks/lik'; import { Observable, from } from 'rxjs'; import { filter } from 'rxjs/operators'; import { rxjs } from '@pushrocks/smartrx'; +import { UniverseConnection } from './smartuniverse.classes.universeconnection'; /** * universe store handles the creation, storage and retrieval of messages. @@ -29,6 +30,8 @@ export class UniverseCache { */ public channelMap = new Objectmap(); + public connectionMap = new plugins.lik.Objectmap(); + /** * allows messages to be processed in a blacklist mode for further analysis */ diff --git a/ts/smartuniverse.classes.universechannel.ts b/ts/smartuniverse.classes.universechannel.ts index 0f9e87f..afc256b 100644 --- a/ts/smartuniverse.classes.universechannel.ts +++ b/ts/smartuniverse.classes.universechannel.ts @@ -1,6 +1,5 @@ import * as plugins from './smartuniverse.plugins'; -import { Objectmap } from '@pushrocks/lik'; import { UniverseCache } from './smartuniverse.classes.universecache'; import { UniverseMessage } from './smartuniverse.classes.universemessage'; @@ -41,6 +40,13 @@ export class UniverseChannel { } } + /** + * a static message authorization function that takes the UniverseCache + * (where messages and channels are stored and their lifetime is managed) + * and the universemessage to find a fitting channel for the message + * @param universeCacheArg + * @param universeMessageArg + */ public static authorizeAMessageForAChannel( universeCacheArg: UniverseCache, universeMessageArg: UniverseMessage @@ -56,6 +62,7 @@ export class UniverseChannel { } else { universeMessageArg.authenticated = false; universeMessageArg.universeChannelList.add(universeCacheArg.blackListChannel); + console.log('message not valid'); } } @@ -79,7 +86,9 @@ export class UniverseChannel { } /** - * authenticates a client on the server side + * authenticates a client on the server side by matching + * # the messages channelName against the unverseChannel's name + * # the messages password against the universeChannel's password */ public authenticate(universeMessageArg: UniverseMessage): boolean { return ( diff --git a/ts/smartuniverse.classes.universeconnection.ts b/ts/smartuniverse.classes.universeconnection.ts index 1e40451..009636f 100644 --- a/ts/smartuniverse.classes.universeconnection.ts +++ b/ts/smartuniverse.classes.universeconnection.ts @@ -1,12 +1,87 @@ import * as plugins from './smartuniverse.plugins'; +import * as interfaces from './interfaces'; import { UniverseChannel } from './smartuniverse.classes.universechannel'; +import { UniverseCache } from './smartuniverse.classes.universecache'; /** * represents a connection to the universe */ export class UniverseConnection { - public terminatedDeferred = plugins.smartpromise.defer(); + /** + * + * @param universeConnectionArg + */ + public static async addConnectionToCache( + universeCache: UniverseCache, + universeConnectionArg: UniverseConnection + ) { + let universeConnection = universeConnectionArg; + universeConnection = await UniverseConnection.deduplicateUniverseConnection( + universeCache, + universeConnection + ); + universeConnection = await UniverseConnection.authenticateAuthenticationRequests( + universeConnection + ); + } + /** + * deduplicates UniverseConnections + */ + public static async deduplicateUniverseConnection( + universeCache: UniverseCache, + universeConnectionArg: UniverseConnection + ): Promise { + let connectionToReturn: UniverseConnection; + universeCache.connectionMap.forEach(async existingConnection => { + if (existingConnection.socketConnection === universeConnectionArg.socketConnection) { + connectionToReturn = await this.mergeUniverseConnections( + existingConnection, + universeConnectionArg + ); + } + }); + if (!connectionToReturn) { + connectionToReturn = universeConnectionArg; + } + return connectionToReturn; + } + + /** + * authenticate AuthenticationRequests + */ + public static authenticateAuthenticationRequests( + universeConnectionArg + ): Promise { + // TODO: authenticate connections + return universeConnectionArg; + } + + /** + * merges two UniverseConnections + */ + public static mergeUniverseConnections( + connectionArg1: UniverseConnection, + connectionArg2: UniverseConnection + ) { + // TODO: merge connections + return connectionArg1; + } + + /** + * finds a UniverseConnection by providing a socket connection + */ + public static findUniverseConnectionBySocketConnection( + universeCache: UniverseCache, + socketConnectionArg: plugins.smartsocket.SocketConnection + ): UniverseConnection { + const universeConnection = universeCache.connectionMap.find(universeConnectionArg => { + return universeConnectionArg.socketConnection === socketConnectionArg; + }); + return universeConnection; + } + + public terminatedDeferred = plugins.smartpromise.defer(); /** * the socketClient to ping @@ -20,16 +95,16 @@ export class UniverseConnection { /** * terminates the connection */ - public terminateConnection () { + public terminateConnection() { this.socketConnection.socket.disconnect(); this.terminatedDeferred.resolve(); } constructor(optionsArg: { socketConnection: plugins.smartsocket.SocketConnection; - authenticationRequests + authenticationRequests: interfaces.IServerCallSubscribeActionPayload[]; }) { // TODO: check if this is correct - this.socketConnection.socket.disconnect(); + this.socketConnection = optionsArg.socketConnection; } } diff --git a/ts/smartuniverse.classes.universeconnectionmanager.ts b/ts/smartuniverse.classes.universeconnectionmanager.ts deleted file mode 100644 index 0f1d678..0000000 --- a/ts/smartuniverse.classes.universeconnectionmanager.ts +++ /dev/null @@ -1,48 +0,0 @@ -import * as plugins from './smartuniverse.plugins'; -import { UniverseConnection } from './smartuniverse.classes.universeconnection'; - -/** - * manages connections to a universe - */ -export class UniverseConnectionManager { - public connectionMap = new plugins.lik.Objectmap(); - - public async addConnection(universeConnectionArg: UniverseConnection) { - let universeConnection = universeConnectionArg; - universeConnection = await this.deduplicateUniverseConnection(universeConnection); - universeConnection = await this.authenticateAuthenticationRequests(universeConnection); - } - - /** - * deduplicates UniverseConnections - */ - public async deduplicateUniverseConnection (universeConnectionArg: UniverseConnection): Promise { - let connectionToReturn: UniverseConnection; - this.connectionMap.forEach(async existingConnection => { - if (existingConnection.socketConnection = universeConnectionArg.socketConnection) { - connectionToReturn = await this.mergeUniverseConnections(existingConnection, universeConnectionArg); - } - }); - if (!connectionToReturn) { - connectionToReturn = universeConnectionArg; - } - return connectionToReturn; - - } - - /** - * authenticate AuthenticationRequests - */ - public authenticateAuthenticationRequests(universeConnectionArg): Promise { - // TODO: authenticate connections - return universeConnectionArg; - } - - /** - * merges two UniverseConnections - */ - public mergeUniverseConnections (connectionArg1: UniverseConnection, connectionArg2: UniverseConnection) { - // TODO: merge connections - return connectionArg1; - } -} diff --git a/ts/smartuniverse.classes.universemessage.ts b/ts/smartuniverse.classes.universemessage.ts index 02d3897..d6d81c7 100644 --- a/ts/smartuniverse.classes.universemessage.ts +++ b/ts/smartuniverse.classes.universemessage.ts @@ -14,6 +14,10 @@ import { IUniverseMessage } from './interfaces'; * acts as a container to save message states like authentication status */ export class UniverseMessage implements interfaces.IUniverseMessage { + public static createMessageFromPayload(dataArg: interfaces.IUniverseMessage) { + return new UniverseMessage(dataArg); + } + public id: string; public timestamp: number;