fwsp-umf-message
Advanced tools
Comparing version 0.2.1 to 0.3.0
333
index.js
@@ -10,25 +10,23 @@ 'use strict'; | ||
const shortToLong = { | ||
frm: 'from', | ||
ts: 'timestamp', | ||
ver: 'version', | ||
bdy: 'body' | ||
}, longToShort = { | ||
from: 'frm', | ||
timestamp: 'ts', | ||
version: 'ver', | ||
body: 'bdy' | ||
}; | ||
class UMFMessage { | ||
constructor() { | ||
this.message = {}; | ||
} | ||
/** | ||
* @name _getTimeStamp | ||
* @name getMessage | ||
* @summary Returns a plain-old JavaScript object | ||
* @return {object} obj - a Plain old JavaScript Object. | ||
*/ | ||
getMessage() { | ||
return Object.assign({}, this.message); | ||
} | ||
/** | ||
* @name getTimeStamp | ||
* @summary retrieve an ISO 8601 timestamp | ||
* @private | ||
* @return {string} timestamp - ISO 8601 timestamp | ||
*/ | ||
_getTimeStamp() { | ||
getTimeStamp() { | ||
return moment().toISOString(); | ||
@@ -55,76 +53,4 @@ } | ||
/** | ||
* @name createMessage | ||
* @summary Create a UMF style message. | ||
* @description This is a helper function which helps format a UMF style message. | ||
* The caller is responsible for ensuring that required fields such as | ||
* "to", "from" and "body" are provided either before or after using | ||
* this function. | ||
* @param {object} message - optional message overrides. | ||
* @param {boolean} [shortFormat=false] - optional flag to use UMF short form syntax. | ||
* @return {object} message - a UMF formatted message. | ||
*/ | ||
createMessage(message, shortFormat=false) { | ||
if (shortFormat) { | ||
return this.createMessageShort(message); | ||
} | ||
let msg = Object.assign({ | ||
mid: this.createMessageID(), | ||
timestamp: this._getTimeStamp(), | ||
version: UMF_VERSION | ||
}, message || {}); | ||
return new Proxy(msg, { | ||
get: (obj, prop) => { | ||
return prop in shortToLong ? obj[shortToLong[prop]] : obj[prop]; | ||
}, | ||
set: (obj, prop, value) => { | ||
if (prop in shortToLong) { | ||
obj[shortToLong[prop]] = value; | ||
} else { | ||
obj[prop] = value; | ||
} | ||
return true; | ||
} | ||
}); | ||
} | ||
/** | ||
* @name createMessageShort | ||
* @summary createMessage with short fields | ||
* @param {object} message - optional message overrides. | ||
* @return {object} message - a UMF formatted short-form message. | ||
*/ | ||
createMessageShort(message) { | ||
let msg = Object.assign({ | ||
mid: this.createShortMessageID(), | ||
ts: this._getTimeStamp(), | ||
ver: UMF_VERSION | ||
}, message || {}); | ||
return new Proxy(msg, { | ||
get: (obj, prop) => { | ||
return prop in longToShort ? obj[longToShort[prop]] : obj[prop]; | ||
}, | ||
set: (obj, prop, value) => { | ||
if (prop in longToShort) { | ||
obj[longToShort[prop]] = value; | ||
} else { | ||
obj[prop] = value; | ||
} | ||
return true; | ||
} | ||
}); | ||
} | ||
/** | ||
* @name toObject | ||
* @param {object} message - message to be converted | ||
* @return {object} unproxied message object | ||
*/ | ||
toObject(message) { | ||
let ret = {}; | ||
Object.keys(message).forEach(k => ret[k] = message[k]); | ||
return ret; | ||
} | ||
/** | ||
* @name toJSON | ||
@@ -135,3 +61,3 @@ * @param {object} message - message to be converted | ||
toJSON(message) { | ||
return Utils.safeJSONStringify(this.toObject(message)); | ||
return Utils.safeJSONStringify(this.message); | ||
} | ||
@@ -142,47 +68,43 @@ | ||
* @summary convert a long message to a short one | ||
* @param {object} message - message to be converted | ||
* @return {object} converted message | ||
*/ | ||
toShort(message) { | ||
let convertedMessage = Object.assign({}, message); | ||
(message.to) && (convertedMessage.to = message.to); | ||
(message.from) && (convertedMessage.frm = message.from); | ||
(message.mid) && (convertedMessage.mid = message.mid); | ||
(message.rmid) && (convertedMessage.rmid = message.rmid); | ||
(message.timestamp) && (convertedMessage.ts = message.timestamp); | ||
(message.version) && (convertedMessage.ver = message.version); | ||
(message.via) && (convertedMessage.via = message.via); | ||
(message['for']) && (convertedMessage['for'] = message['for']); | ||
(message.body) && (convertedMessage.bdy = message.body); | ||
return convertedMessage; | ||
toShort() { | ||
let message = {}; | ||
if (this.message['to']) { | ||
message['to'] = this.message['to']; | ||
} | ||
if (this.message['from']) { | ||
message['frm'] = this.message['from']; | ||
} | ||
if (this.message['mid']) { | ||
message['mid'] = this.message['mid']; | ||
} | ||
if (this.message['rmid']) { | ||
message['rmid'] = this.message['rmid']; | ||
} | ||
if (this.message['timestamp']) { | ||
message['ts'] = this.message['timestamp']; | ||
} | ||
if (this.message['version']) { | ||
message['ver'] = this.message['version']; | ||
} | ||
if (this.message['via']) { | ||
message['via'] = this.message['via']; | ||
} | ||
if (this.message['for']) { | ||
message['for'] = this.message['for']; | ||
} | ||
if (this.message['body']) { | ||
message.bdy = this.message['body']; | ||
} | ||
return message; | ||
} | ||
/** | ||
* @name toLong | ||
* @summary convert a short message to a long one | ||
* @param {object} message - message to be converted | ||
* @return {object} converted message | ||
*/ | ||
toLong(message) { | ||
let convertedMessage = Object.assign({}, message); | ||
(message.to) && (convertedMessage.to = message.to); | ||
(message.frm) && (convertedMessage.from = message.frm); | ||
(message.mid) && (convertedMessage.mid = message.mid); | ||
(message.rmid) && (convertedMessage.rmid = message.rmid); | ||
(message.ts) && (convertedMessage.timestamp = message.ts); | ||
(message.ver) && (convertedMessage.version = message.ver); | ||
(message.via) && (convertedMessage.via = message.via); | ||
(message['for']) && (convertedMessage['for'] = message['for']); | ||
(message.bdy) && (convertedMessage.body = message.bdy); | ||
return convertedMessage; | ||
} | ||
/** | ||
* @name validateMessage | ||
* @summary Validates that a UMF message has required fields | ||
* @param {object} message - UMF formatted message | ||
* @return {boolean} response - returns true is valid otherwise false | ||
*/ | ||
validateMessage(message) { | ||
if ((!message.from && !message.frm) || !message.to || (!message.body && !message.bdy)) { | ||
validateMessage() { | ||
if (!this.message.from || !this.message.to || !this.message.body) { | ||
return false; | ||
@@ -193,80 +115,111 @@ } else { | ||
} | ||
} | ||
/** | ||
* @name getMessageBody | ||
* @summary Return the body from a UMF message | ||
* @param {object} message - UMF message | ||
* @return {object} body - UMF message body | ||
*/ | ||
getMessageBody(message) { | ||
return Object.assign({}, message.body); | ||
function createMessageInstance(message) { | ||
let proxy = new Proxy(new UMFMessage(), { | ||
get: function(target, name, receiver) { | ||
return name in target ? | ||
target[name] : target.message[name]; | ||
}, | ||
set: (obj, prop, value) => { | ||
obj.message[prop] = value; | ||
return true; | ||
} | ||
}); | ||
if (message.to) { | ||
proxy.to = message.to; | ||
} | ||
if (message.from || message.frm) { | ||
proxy.from = message.from || message.frm; | ||
} | ||
proxy.mid = message.mid || proxy.createMessageID(); | ||
if (message.rmid) { | ||
proxy.rmid = message.rmid; | ||
} | ||
proxy.timestamp = message.timestamp || message.ts || proxy.getTimeStamp(); | ||
proxy.version = message.version || message.ver || UMF_VERSION; | ||
if (message.via) { | ||
proxy.via = message.via; | ||
} | ||
if (message['for']) { | ||
proxy['for'] = message['for']; | ||
} | ||
if (message.body || message.bdy) { | ||
proxy.body = message.body || message.bdy; | ||
} | ||
return proxy; | ||
} | ||
/** | ||
* @name parseRoute | ||
* @summary parses message route strings | ||
* @private | ||
* @param {string} toValue - string to be parsed | ||
* @return {object} object - containing route parameters. If the | ||
* object contains an error field then the route | ||
* isn't valid. | ||
*/ | ||
parseRoute(toValue) { | ||
let serviceName = ''; | ||
let httpMethod; | ||
let apiRoute = ''; | ||
let error; | ||
let urlRoute = toValue; | ||
let instance = ''; | ||
let subID = ''; | ||
/** | ||
* @name parseRoute | ||
* @summary parses message route strings | ||
* @private | ||
* @param {string} toValue - string to be parsed | ||
* @return {object} object - containing route parameters. If the | ||
* object contains an error field then the route | ||
* isn't valid. | ||
*/ | ||
function parseRoute(toValue) { | ||
let serviceName = ''; | ||
let httpMethod; | ||
let apiRoute = ''; | ||
let error; | ||
let urlRoute = toValue; | ||
let instance = ''; | ||
let subID = ''; | ||
let atPos = urlRoute.indexOf('@'); | ||
if (atPos > -1) { | ||
instance = urlRoute.substring(0, atPos); | ||
urlRoute = urlRoute.substring(atPos + 1); | ||
let segments = instance.split('-'); | ||
if (segments.length > 0) { | ||
instance = segments[0]; | ||
subID = segments[1]; | ||
} | ||
let atPos = urlRoute.indexOf('@'); | ||
if (atPos > -1) { | ||
instance = urlRoute.substring(0, atPos); | ||
urlRoute = urlRoute.substring(atPos + 1); | ||
let segments = instance.split('-'); | ||
if (segments.length > 0) { | ||
instance = segments[0]; | ||
subID = segments[1]; | ||
} | ||
let segments = urlRoute.split(':'); | ||
if (segments.length < 1) { | ||
error = 'route field has invalid number of routable segments'; | ||
} else { | ||
if (segments[0].indexOf('http') === 0) { | ||
let url = `${segments[0]}:${segments[1]}`; | ||
segments.shift(); | ||
segments[0] = url; | ||
} | ||
let segments = urlRoute.split(':'); | ||
if (segments.length < 1) { | ||
error = 'route field has invalid number of routable segments'; | ||
} else { | ||
if (segments[0].indexOf('http') === 0) { | ||
let url = `${segments[0]}:${segments[1]}`; | ||
segments.shift(); | ||
segments[0] = url; | ||
} | ||
serviceName = segments[0]; | ||
segments.shift(); | ||
apiRoute = segments.join(':'); | ||
let s1 = apiRoute.indexOf('['); | ||
if (s1 === 0) { | ||
let s2 = apiRoute.indexOf(']'); | ||
if (s2 < 0) { | ||
error = 'route field has ill-formed HTTP method verb in segment'; | ||
} else { | ||
httpMethod = apiRoute.substring(s1 + 1, s2).toLowerCase(); | ||
} | ||
serviceName = segments[0]; | ||
segments.shift(); | ||
apiRoute = segments.join(':'); | ||
let s1 = apiRoute.indexOf('['); | ||
if (s1 === 0) { | ||
let s2 = apiRoute.indexOf(']'); | ||
if (s2 < 0) { | ||
error = 'route field has ill-formed HTTP method verb in segment'; | ||
} else { | ||
httpMethod = apiRoute.substring(s1 + 1, s2).toLowerCase(); | ||
if (!error) { | ||
let s3 = httpMethod.length; | ||
if (s3 > 0) { | ||
apiRoute = apiRoute.substring(s3 + 2, apiRoute.length); | ||
} | ||
if (!error) { | ||
let s3 = httpMethod.length; | ||
if (s3 > 0) { | ||
apiRoute = apiRoute.substring(s3 + 2, apiRoute.length); | ||
} | ||
} | ||
} | ||
} | ||
return { | ||
instance, | ||
subID, | ||
serviceName, | ||
httpMethod, | ||
apiRoute, | ||
error | ||
}; | ||
} | ||
return { | ||
instance, | ||
subID, | ||
serviceName, | ||
httpMethod, | ||
apiRoute, | ||
error | ||
}; | ||
} | ||
module.exports = new UMFMessage(); | ||
/** | ||
* Return an ES6 Proxy object which provides access to message fields. | ||
*/ | ||
module.exports = { | ||
createMessage: createMessageInstance, | ||
parseRoute: parseRoute | ||
}; |
{ | ||
"name": "fwsp-umf-message", | ||
"version": "0.2.1", | ||
"version": "0.3.0", | ||
"description": "umf-message: a tool for creating and working with UMF style messages", | ||
@@ -5,0 +5,0 @@ "author": "Carlos Justiniano", |
153
README.md
@@ -14,157 +14,38 @@ # UMF Message [![Build Status](https://travis-ci.org/flywheelsports/fwsp-umf-message.svg?branch=master)](https://travis-ci.org/flywheelsports/fwsp-umf-message) | ||
```javascript | ||
const Utils = require('fwsp-umf-message'); | ||
let instanceID = 'fa1ae8d5-86fc-44af-aad8-cd2740aef041'; | ||
const UMFMessage = require('fwsp-umf-message'); | ||
let msg = UMFMessage.createMessage({ | ||
to: `${instanceID}@test-service:[GET]/v1/somedata`, | ||
to: `test-service:[GET]/v1/somedata`, | ||
from: 'client:/', | ||
body: {} | ||
body: { | ||
somevalues: [1,2,3,4] | ||
} | ||
}); | ||
``` | ||
## Tests | ||
You can also access message fields for getting and setting: | ||
Tests can be found in the `specs` folder. | ||
```shell | ||
$ npm test | ||
``` | ||
## API | ||
See: [API documentation](api.md) | ||
### createMessageID - Returns a UUID for use with messages | ||
```javascript | ||
/** | ||
* @name createMessageID | ||
* @summary Returns a UUID for use with messages | ||
* @return {string} uuid - UUID | ||
*/ | ||
createMessageID() | ||
console.log('msg.to', msg.to); | ||
msg.to = 'router:/'; | ||
console.log('msg.to', msg.to); | ||
``` | ||
### createShortMessageID - Returns a short form UUID for use with messages | ||
To retrieve an entire message object using: | ||
```javascript | ||
/** | ||
* @name createShortMessageID | ||
* @summary Returns a short form UUID for use with messages | ||
* @return {string} uuid - UUID | ||
*/ | ||
createShortMessageID() | ||
console.log(msg.getMessage()); | ||
``` | ||
### createMessage - Create a UMF style message | ||
And you get get a JSON string using: | ||
```javascript | ||
/** | ||
* @name createMessage | ||
* @summary Create a UMF style message. | ||
* @description This is a helper function which helps format a UMF style message. | ||
* The caller is responsible for ensuring that required fields such as | ||
* "to", "from" and "body" are provided either before or after using | ||
* this function. | ||
* @param {object} message - optional message overrides. | ||
* @param {boolean} shortFormat - optional flag to use UMF short form syntax. | ||
* @return {object} message - a UMF formatted message. | ||
*/ | ||
createMessage(message, shortFormat=false) | ||
console.log(msg.toJSON()) | ||
``` | ||
### createMessageShort - Create a short-format UMF message | ||
## Tests | ||
```javascript | ||
/** | ||
* @name createMessageShort | ||
* @summary createMessage with short fields | ||
* @param {object} message - optional message overrides. | ||
* @return {object} message - a UMF formatted short-form message. | ||
*/ | ||
createMessageShort(message) | ||
``` | ||
Tests can be found in the `specs` folder. | ||
### toObject - get message as Object (no Proxy) | ||
```javascript | ||
/** | ||
* @name toObject | ||
* @param {object} message - message to be converted | ||
* @return {object} unproxied message object | ||
*/ | ||
toObject(message) | ||
```shell | ||
$ npm test | ||
``` | ||
### toJSON - serialize message to JSON string | ||
```javascript | ||
/** | ||
* @name toJSON | ||
* @param {object} message - message to be converted | ||
* @return {string} JSON version of message | ||
*/ | ||
toJSON(message) | ||
``` | ||
### toShort - convert a long message to a short one | ||
```javascript | ||
/** | ||
* @name toShort | ||
* @summary convert a long message to a short one | ||
* @param {object} message - message to be converted | ||
* @return {object} converted message | ||
*/ | ||
toShort(message) | ||
``` | ||
### toLong - convert a short message to a long one | ||
```javascript | ||
/** | ||
* @name toLong | ||
* @summary convert a short message to a long one | ||
* @param {object} message - message to be converted | ||
* @return {object} converted message | ||
*/ | ||
toLong(message) | ||
``` | ||
### validateMessage - Validates that a UMF message has required fields | ||
```javascript | ||
/** | ||
* @name validateMessage | ||
* @summary Validates that a UMF message has required fields | ||
* @param {object} message - UMF formatted message | ||
* @return {boolean} response - returns true is valid otherwise false | ||
*/ | ||
validateMessage(message) | ||
``` | ||
### getMessageBody - Return the body from a UMF message | ||
```javascript | ||
/** | ||
* @name getMessageBody | ||
* @summary Return the body from a UMF message | ||
* @param {object} message - UMF message | ||
* @return {object} body - UMF message body | ||
*/ | ||
getMessageBody(message) | ||
``` | ||
### parseRoute - parses message route strings | ||
```javascript | ||
/** | ||
* @name parseRoute | ||
* @summary parses message route strings | ||
* @private | ||
* @param {string} toValue - string to be parsed | ||
* @return {object} object - containing route parameters. If the | ||
* object contains an error field then the route | ||
* isn't valid. | ||
*/ | ||
parseRoute(toValue) | ||
``` |
@@ -16,12 +16,12 @@ 'use strict'; | ||
let msg = UMFMessage.createMessage({}); | ||
expect(msg).to.have.property('mid'); | ||
expect(msg).to.have.property('timestamp'); | ||
expect(msg).to.have.property('version'); | ||
expect(msg.mid).to.not.be.undefined; | ||
expect(msg.timestamp).to.not.be.undefined; | ||
expect(msg.version).to.not.be.undefined; | ||
}); | ||
it('should have standard properties for short messages', () => { | ||
let msg = UMFMessage.createMessageShort({}); | ||
expect(msg).to.have.property('mid'); | ||
expect(msg).to.have.property('ts'); | ||
expect(msg).to.have.property('ver'); | ||
let msg = UMFMessage.createMessage({}).toShort(); | ||
expect(msg.mid).to.not.be.undefined; | ||
expect(msg.ts).to.not.be.undefined; | ||
expect(msg.ver).to.not.be.undefined; | ||
}); | ||
@@ -32,3 +32,3 @@ }); | ||
it('should convert a short form message to a long form message', () => { | ||
let msg = UMFMessage.createMessageShort({ | ||
let msg = UMFMessage.createMessage({ | ||
to: 'someservice:/', | ||
@@ -40,5 +40,4 @@ frm: 'tester', | ||
}); | ||
let longFormMessage = UMFMessage.toLong(msg); | ||
expect(longFormMessage).to.have.property('from'); | ||
expect(longFormMessage).to.have.property('body'); | ||
expect(msg.from).to.not.be.undefined; | ||
expect(msg.body).to.not.be.undefined; | ||
}); | ||
@@ -53,5 +52,7 @@ it('should convert a long form message to a short form message', () => { | ||
}); | ||
let shortFormMessage = UMFMessage.toShort(msg); | ||
let shortFormMessage = msg.toShort(); | ||
expect(shortFormMessage).to.have.property('frm'); | ||
expect(shortFormMessage).to.have.property('bdy'); | ||
expect(shortFormMessage).not.to.have.property('from'); | ||
expect(shortFormMessage).not.to.have.property('body'); | ||
}); | ||
@@ -63,3 +64,3 @@ }); | ||
let msg = UMFMessage.createMessage({}); | ||
let ret = UMFMessage.validateMessage(msg); | ||
let ret = msg.validateMessage(); | ||
expect(ret).to.be.false; | ||
@@ -70,3 +71,3 @@ expect(msg['from']).to.be.undefined; | ||
let msg = UMFMessage.createMessage({}); | ||
let ret = UMFMessage.validateMessage(msg); | ||
let ret = msg.validateMessage(); | ||
expect(ret).to.be.false; | ||
@@ -77,3 +78,3 @@ expect(msg['to']).to.be.undefined; | ||
let msg = UMFMessage.createMessage({}); | ||
let ret = UMFMessage.validateMessage(msg); | ||
let ret = msg.validateMessage(); | ||
expect(ret).to.be.false; | ||
@@ -89,3 +90,3 @@ expect(msg['body']).to.be.undefined; | ||
}); | ||
let ret = UMFMessage.validateMessage(msg); | ||
let ret = msg.validateMessage(); | ||
expect(ret).to.be.true; | ||
@@ -126,34 +127,5 @@ }); | ||
describe('proxied object', () => { | ||
it('should return values for long-form keys on short-form message ', () => { | ||
let msg = UMFMessage.createMessageShort({ | ||
to: 'someservice:/', | ||
frm: 'tester', | ||
bdy: { | ||
val: 'some value' | ||
} | ||
}); | ||
expect(msg.frm).to.equal(msg.from); | ||
expect(msg.bdy).to.equal(msg.body); | ||
expect(msg.ts).to.equal(msg.timestamp); | ||
expect(msg.ver).to.equal(msg.version); | ||
}); | ||
it('should return values for short-form keys on long-form message', () => { | ||
let msg = UMFMessage.createMessage({ | ||
to: 'someservice:/', | ||
from: 'tester', | ||
body: { | ||
val: 'some value' | ||
} | ||
}); | ||
expect(msg.from).to.equal(msg.frm); | ||
expect(msg.body).to.equal(msg.bdy); | ||
expect(msg.timestamp).to.equal(msg.ts); | ||
expect(msg.version).to.equal(msg.ver); | ||
}); | ||
}); | ||
describe('toJSON', () => { | ||
it('should return long-form keys in JSON for a long-form message', () => { | ||
it('should return valid JSON for message', () => { | ||
let msg = UMFMessage.createMessage({ | ||
@@ -166,19 +138,6 @@ to: 'someservice:/', | ||
}); | ||
let json = UMFMessage.toJSON(msg); | ||
let json = msg.toJSON(msg); | ||
let parsed = Utils.safeJSONParse(json); | ||
expect(parsed).to.have.property('from'); | ||
}); | ||
it('should return short-form keys in JSON for a short-form message', () => { | ||
let msg = UMFMessage.createMessageShort({ | ||
to: 'someservice:/', | ||
frm: 'tester', | ||
bdy: { | ||
val: 'some value' | ||
} | ||
}); | ||
let json = UMFMessage.toJSON(msg); | ||
let parsed = Utils.safeJSONParse(json); | ||
expect(parsed).to.have.property('frm'); | ||
}); | ||
}); |
18762
328
51