Compare commits

..

13 Commits

Author SHA1 Message Date
a8a91b4db2 1.0.42 2019-04-22 23:23:36 +02:00
67c4b06c4d fix(core): update 2019-04-22 23:23:36 +02:00
7693b52066 1.0.41 2019-04-22 23:11:52 +02:00
30a02ae48b fix(core): update 2019-04-22 23:11:51 +02:00
241182ed2e 1.0.40 2019-04-22 22:04:53 +02:00
3d82038ec3 fix(core): update 2019-04-22 22:04:52 +02:00
300d62ed12 1.0.39 2019-04-22 13:06:02 +02:00
a5e849aa17 fix(core): update 2019-04-22 13:06:01 +02:00
83807d7c5c 1.0.38 2019-04-22 09:58:36 +02:00
39d3a0f2f8 fix(core): update 2019-04-22 09:58:36 +02:00
904a48d414 1.0.37 2019-04-12 20:50:44 +02:00
e2acb28756 1.0.36 2019-04-11 18:59:46 +02:00
92e4379bd2 fix(core): update 2019-04-11 18:59:45 +02:00
13 changed files with 172 additions and 82 deletions

21
package-lock.json generated
View File

@ -1,6 +1,6 @@
{
"name": "@pushrocks/smartuniverse",
"version": "1.0.35",
"version": "1.0.42",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@ -386,6 +386,17 @@
"luxon": "^1.12.1"
}
},
"@pushrocks/smartunique": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/@pushrocks/smartunique/-/smartunique-3.0.1.tgz",
"integrity": "sha512-xBu9ZB4C0BA0S/pbFFZn2ItPfnodPKpzrYIq1yN5XDs6OaookwcDF/iBwfS9+EYMSPENC9wAsOxg2RGMm4Qicw==",
"requires": {
"@types/shortid": "^0.0.29",
"@types/uuid": "^3.0.0",
"shortid": "^2.2.8",
"uuid": "^3.1.0"
}
},
"@pushrocks/tapbundle": {
"version": "3.0.9",
"resolved": "https://verdaccio.lossless.one/@pushrocks%2ftapbundle/-/tapbundle-3.0.9.tgz",
@ -566,6 +577,14 @@
"@types/node": "*"
}
},
"@types/uuid": {
"version": "3.4.4",
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-3.4.4.tgz",
"integrity": "sha512-tPIgT0GUmdJQNSHxp0X2jnpQfBSTfGxUMc/2CXBU2mnyTFVYVa2ojpoQ74w0U2yn2vw3jnC640+77lkFFpdVDw==",
"requires": {
"@types/node": "*"
}
},
"@types/vinyl": {
"version": "2.0.3",
"resolved": "https://verdaccio.lossless.one/@types%2fvinyl/-/vinyl-2.0.3.tgz",

View File

@ -1,6 +1,6 @@
{
"name": "@pushrocks/smartuniverse",
"version": "1.0.35",
"version": "1.0.42",
"private": false,
"description": "messaging service for your micro services",
"main": "dist/index.js",
@ -33,6 +33,7 @@
"@pushrocks/smartrequest": "^1.1.14",
"@pushrocks/smartrx": "^2.0.3",
"@pushrocks/smartsocket": "^1.1.27",
"@pushrocks/smarttime": "^3.0.7"
"@pushrocks/smarttime": "^3.0.7",
"@pushrocks/smartunique": "^3.0.1"
}
}

View File

@ -27,6 +27,10 @@ Use TypeScript for best in class instellisense.
Think WhatsApp, but for your microservices architecture. It allows your services to securely talk to each other in **private, shielded channels** without having to expose anything to the outside world. This allows the use of **reactive programming across your entire stack**.
### Server side
every universe has a server that manages messages.
Think Kafka, but without Kafka.
```typescript
import * as smartuniverse from '@pushrocks/smartuniverse';
@ -41,6 +45,9 @@ myUniverse.addChannel('awesomeChannel2', 'jhkjhfsdf87eerkjslkfja9');
myUniverse.start(8765); // start the server and provide the port on which to listen on
```
### Client side
All your microservices represents clients in the universe that may talk to each other using the universe server.
For further information read the linked docs at the top of this readme.
> MIT licensed | **©** [Lossless GmbH](https://lossless.gmbh)

View File

@ -5,13 +5,13 @@ import * as smartuniverse from '../ts/index';
import { Observable } from 'rxjs';
let testUniverse: smartuniverse.Universe;
let testUniverseClient: smartuniverse.ClientUniverse;
let testClientUniverse: smartuniverse.ClientUniverse;
let testClientChannel: smartuniverse.ClientUniverseChannel;
const testChannelData = {
channelName: 'awesomeTestChannel',
channelPass: 'awesomeChannelPAss'
}
};
tap.test('first test', async () => {
testUniverse = new smartuniverse.Universe({
@ -25,31 +25,42 @@ tap.test('add a message to the SmartUniverse', async () => {
// testing message handling
tap.test('create smartuniverse client', async () => {
testUniverseClient = new smartuniverse.ClientUniverse({
testClientUniverse = new smartuniverse.ClientUniverse({
serverAddress: 'http://localhost:8765'
});
expect(testUniverseClient).to.be.instanceof(smartuniverse.ClientUniverse);
expect(testClientUniverse).to.be.instanceof(smartuniverse.ClientUniverse);
});
tap.test('should add a channel to the universe', async () => {
await testUniverse.addChannel('testChannel', 'testPassword');
await testUniverse.addChannel(
testChannelData.channelName,
testChannelData.channelPass
);
});
tap.test('should add the same channel to the client universe in the same way', async () => {
await testClientUniverse.addChannel(
testChannelData.channelName,
testChannelData.channelPass
);
});
tap.test('should get a observable correctly', async () => {
testClientChannel = await testUniverseClient.getChannel(testChannelData.channelName, testChannelData.channelPass);
testClientChannel = await testClientUniverse.getChannel(testChannelData.channelName);
expect(testClientChannel).to.be.instanceof(smartuniverse.ClientUniverseChannel);
});
tap.test('should send a message correctly', async () => {
await testUniverseClient.sendMessage('greeting', {
anyBool: true
await testClientUniverse.sendMessage({
messageText: 'hello',
targetChannelName: testChannelData.channelName
});
});
tap.test('should receive a message correctly', async () => {});
tap.test('should disconnect the client correctly', async () => {
testUniverseClient.close();
testClientUniverse.close();
});
tap.test('should end the server correctly', async tools => {

View File

@ -1,7 +1,15 @@
export interface IUniverseMessage {
export interface IMessageCreator {
messageText: string;
targetChannelName: string;
passphrase: string;
payload?: string | number | any;
payloadStringType?: 'Buffer' | 'string' | 'object';
targetChannelName: string;
}
export interface IUniverseMessage extends IMessageCreator {
id: string;
/**
* time of creation
*/
timestamp: number;
passphrase: string;
}

View File

@ -31,8 +31,46 @@ export class ClientUniverse {
this.options = optionsArg;
}
public async sendMessage(messageArg: interfaces.IUniverseMessage) {
const requestBody: interfaces.IUniverseMessage = messageArg;
/**
* adds a channel to the channelcache
* TODO: verify channel before adding it to the channel cache
*/
public async addChannel (channelNameArg: string, passphraseArg: string) {
const existingChannel = this.getChannel(channelNameArg);
if (existingChannel) {
throw new Error('channel exists');
}
// lets create the channel
ClientUniverseChannel.createClientUniverseChannel(
this,
channelNameArg,
passphraseArg
);
}
/**
* gets a channel from the channelcache
* @param channelName
* @param passphraseArg
*/
public async getChannel(channelName: string): Promise<ClientUniverseChannel> {
await this.checkConnection();
const clientUniverseChannel = this.channelCache.find(channel => {
return channel.name === channelName;
})
return clientUniverseChannel;
}
public async sendMessage(messageArg: interfaces.IMessageCreator) {
const requestBody: interfaces.IUniverseMessage = {
id: plugins.smartunique.shortId(),
timestamp: Date.now(),
passphrase: (await this.getChannel(messageArg.targetChannelName)).passphrase,
...messageArg,
};
const requestBodyString = JSON.stringify(requestBody);
// TODO: User websocket connection if available
const response = await plugins.smartrequest.postJson(`${this.options.serverAddress}/sendmessage` , {
@ -40,21 +78,15 @@ export class ClientUniverse {
});
}
public async getChannel(channelName: string, passphrase): Promise<ClientUniverseChannel> {
await this.checkConnection();
const clientUniverseChannel = await ClientUniverseChannel.createClientUniverseChannel(
this,
channelName
);
this.channelCache.add(clientUniverseChannel);
return clientUniverseChannel;
}
public close() {
this.socketClient.disconnect();
}
private async checkConnection() {
/**
* checks the connection towards a universe server
* since password validation is done through other means, a connection should always be possible
*/
private async checkConnection(): Promise<void> {
if (!this.socketClient && !this.observableIntake) {
const parsedURL = url.parse(this.options.serverAddress);
this.socketClient = new SmartsocketClient({

View File

@ -7,12 +7,20 @@ export class ClientUniverseChannel implements interfaces.IUniverseChannel {
// ======
// STATIC
// ======
/**
* creates a channel and adds it to the cache of clientUniverseArg
* @param clientUniverseArg
* @param channelNameArg
* @param passphraseArg
*/
public static async createClientUniverseChannel(
clientUniverseArg: ClientUniverse,
channelName: string
channelNameArg: string,
passphraseArg: string
): Promise<ClientUniverseChannel> {
const clientChannel = new ClientUniverseChannel(clientUniverseArg);
await clientChannel.transmitSubscription();
const clientChannel = new ClientUniverseChannel(clientUniverseArg, channelNameArg, passphraseArg);
clientUniverseArg.channelCache.add(clientChannel);
await clientChannel.subscribe();
return clientChannel;
}
@ -20,16 +28,24 @@ export class ClientUniverseChannel implements interfaces.IUniverseChannel {
// INSTANCE
// ========
// properties
public name: string;
public passphrase: string;
// refs
public clientUniverse: ClientUniverse;
constructor(clientUniverseArg: ClientUniverse) {
constructor(clientUniverseArg: ClientUniverse, nameArg: string, passphraseArg: string) {
this.clientUniverse = clientUniverseArg;
this.name = nameArg;
this.passphrase = passphraseArg;
}
/**
* subscribes to a channel
* tells the universe about this instances interest into a channel
*/
public async transmitSubscription() {
public async subscribe() {
this.clientUniverse.socketClient;
}
}

View File

@ -6,13 +6,24 @@ export class ClientUniverseMessage implements interfaces.IUniverseMessage {
// ======
// STATIC
// ======
public static createMessageFromPayload(messageArg: string, payloadArg: any) {
public static createMessageFromPayload(messageDescriptor: interfaces.IUniverseMessage) {
};
// ========
// INSTANCE
// ========
public id: string;
public timestamp: number;
public smartTimestamp: plugins.smarttime.TimeStamp;
public messageText: string;
public passphrase: string;
public payload: any;
public payloadStringType;
public targetChannelName: string;
constructor(messageArg, payloadArg) {}
getAsJsonForPayload () {

View File

@ -80,7 +80,8 @@ export class Universe {
// lets create the http request route
this.smartexpressServer.addRoute('/sendmessage', new Handler('POST', async (req, res) => {
this.universeCache.addMessage(req.body);
const universeMessageInstance = new UniverseMessage(req.body);
this.universeCache.addMessage(universeMessageInstance);
}));
// add websocket upgrade
@ -100,7 +101,9 @@ export class Universe {
const SubscriptionSocketFunction = new plugins.smartsocket.SocketFunction({
allowedRoles: [ClientRole],
funcName: 'channelSubscription',
funcDef: () => {} // TODO: implement an action upon connection of clients
funcDef: () => {
console.log('a client connected');
} // TODO: implement an action upon connection of clients
});
// add smartsocket to the running smartexpress app

View File

@ -55,7 +55,7 @@ export class UniverseCache {
public readMessagesYoungerThan(unixTimeArg?: number): Observable<UniverseMessage> {
const messageObservable = from(this.messageMap.getArray()).pipe(
filter(messageArg => {
return messageArg.timestamp.isYoungerThanMilliSeconds(this.destructionTime);
return messageArg.smartTimestamp.isYoungerThanMilliSeconds(this.destructionTime);
})
);
return messageObservable;

View File

@ -83,8 +83,8 @@ export class UniverseChannel {
*/
public authenticate(universeMessageArg: UniverseMessage): boolean {
return (
this.name === universeMessageArg.requestedChannelName &&
this.passphrase === universeMessageArg.requestedChannelPassphrase
this.name === universeMessageArg.targetChannelName &&
this.passphrase === universeMessageArg.passphrase
);
}

View File

@ -7,34 +7,30 @@ import { Timer, TimeStamp } from '@pushrocks/smarttime';
import { Universe } from './smartuniverse.classes.universe';
import { UniverseChannel } from './smartuniverse.classes.universechannel';
import { UniverseCache } from './smartuniverse.classes.universecache';
import { IUniverseMessage } from './interfaces';
/**
* represents a message within a universe
* acts as a container to save message states like authentication status
*/
export class UniverseMessage implements interfaces.IUniverseMessage {
/**
* public and unique id
* numeric ascending
* adheres to time in milliseconds
* -> meaning it describes the time of arrival
* -> two messages received at the same time will count up the second one
* -> avoids duplications of messages
* -> may be changed to nanoseconds to ensure higher throughput
*/
public id: number;
public id: string;
public timestamp: number;
public smartTimestamp: TimeStamp;
public messageText: string;
public passphrase: string;
public payload: any;
public payloadStringType;
public targetChannelName: string;
/**
* the UniverseCache the message is attached to
*/
public universeCache: UniverseCache;
/**
* requestedChannelName
*/
public requestedChannelName: string;
public requestedChannelPassphrase: string;
/**
* enables unprotected grouping of messages for efficiency purposes.
*/
@ -44,21 +40,10 @@ export class UniverseMessage implements interfaces.IUniverseMessage {
* wether the message is authenticated
*/
public authenticated: boolean = null;
/**
* time of creation
* a destruction timer for this message
*/
public timestamp: TimeStamp;
/**
* the actual message
*/
public message: string;
/**
* any attached payloads. Can be of binary format.
*/
public attachedPayload: any;
public destructionTimer: Timer; // a timer to take care of message destruction
/**
@ -66,17 +51,12 @@ export class UniverseMessage implements interfaces.IUniverseMessage {
* @param messageArg
* @param attachedPayloadArg
*/
constructor(
messageArg: string,
requestedChannelNameArg: string,
passphraseArg: string,
attachedPayloadArg: any
) {
this.timestamp = new TimeStamp();
this.message = messageArg;
this.requestedChannelName = requestedChannelNameArg;
this.requestedChannelPassphrase = passphraseArg;
this.attachedPayload = attachedPayloadArg;
constructor(messageDescriptor: IUniverseMessage) {
this.smartTimestamp = new TimeStamp(this.timestamp);
this.messageText = messageDescriptor.messageText;
this.targetChannelName = messageDescriptor.targetChannelName;
this.passphrase = messageDescriptor.passphrase;
this.payload = messageDescriptor.payload;
// prevent memory issues
this.fallBackDestruction();
}
@ -102,7 +82,7 @@ export class UniverseMessage implements interfaces.IUniverseMessage {
/**
* handles bad messages for further analysis
*/
handleAsBadMessage() {
public handleAsBadMessage() {
console.log('received a bad message');
}

View File

@ -14,6 +14,7 @@ import * as smartrequest from '@pushrocks/smartrequest';
import * as smartrx from '@pushrocks/smartrx';
import * as smartsocket from '@pushrocks/smartsocket';
import * as smarttime from '@pushrocks/smarttime';
import * as smartunique from '@pushrocks/smartunique';
export {
lik,
@ -25,5 +26,6 @@ export {
smartrx,
smartrequest,
smartsocket,
smarttime
smarttime,
smartunique
};