onem2m
Version:
oneM2M constants and utilities
457 lines (422 loc) • 11.8 kB
JavaScript
/**
* @file
* @copyright 2016, loocat.
*/
var xml2js = require('xml2js');
var moment = require('moment');
var util = require('util');
var uuid = require('uuid');
var name = require('./name');
var code = require('./code');
var rid = /^[a-z]+-[0-9a-f]{8}-?[0-9a-f]{4}-?[0-5][0-9a-f]{3}-?[089ab][0-9a-f]{3}-?[0-9a-f]{12}$/i;
var isID = (text) => {
return rid.test(text);
}
var isStructured = (path) => {
var arr = path.split('/');
return (arr.length !== 1 || !isID(arr[0]));
}
/**
*
*/
var getDatetime = function (hrLen) {
var tmp = moment().format("YYYYMMDDTHHmmss");
if (hrLen > 0) {
tmp += ('000000000' + process.hrtime()[1]).slice(-9).slice(0, hrLen > 9 ? 9 : hrLen);
}
return tmp;
}
/**
*
*/
var createRequestID = function () {
return util.format('rqi-%s', uuid.v1());
}
/**
*
*/
var createResourceID = function (typeCode) {
return util.format('%s-%s', name.getShort(code.getResourceType(typeCode)), uuid.v4());
}
/**
*
**/
var translateOperation = function (operationType, bindType) {
var toHttp = function (op) {
if (op === 'Create') {
return 'post';
}
else if (op === 'Retrieve') {
return 'get';
}
else if (op === 'Update') {
return 'put';
}
else if (op === 'Delete') {
return 'delete';
}
else if (op === 'Notify') {
// TODO fix this
return 'post';
}
log.error('unknown oneM2M operation: ' + op)
throw op;
}
var toMqtt = function (op) {
return code.getOperation(op);
}
return (bindType && bindType.toLowerCase() === 'http' ? toHttp : toMqtt)(operationType);
}
/**
*
*/
var isSimple = function (ttt) {
return (ttt === 'string' || ttt === 'number' || ttt === 'boolean');
}
/**
*
*/
var attributes = ['rn'];
var attributize = function (pc, cty, level) {
if (typeof level === 'undefined') level = 0;
var tr = {
'xml': (key) => {
if (key && pc[key]) {
var ttt = pc['$'];
if (!ttt) { ttt = {}; pc['$'] = ttt; }
ttt[key] = pc[key].substring(0);
delete pc[key];
}
}
};
if (tr[cty] && pc && isSimple(typeof pc) === false) {
if (Object.keys(pc).length > 0) {
attributes.forEach(tr[cty]);
Object.keys(pc).forEach((key) => {
if (key !== '$' && pc[key]) {
pc[key] = attributize(pc[key], cty, level + 1);
}
});
}
else if (Array.isArray(pc)) {
pc.forEach((ttt) => {
ttt = attributize(ttt, cty, level + 1);
});
}
}
return pc;
}
/**
*
*/
var wrapMessage = function (root, content, format, long) {
content = attributize(content, format);
var category = ['PrimitiveRootElements', 'ResourceTypes', 'ComplexDataTypesMembers'];
var wrapped = undefined;
var wrapType = (long ? name.getLong : name.getShort)(root, category);
var message = (long ? toLongName(content, true) : content);
if (format && format.indexOf('xml') > -1) {
var atr = message['$'];
if (!atr) { atr = {}; message['$'] = atr; }
atr['xmlns:m2m'] = 'http://www.onem2m.org/xml/protocols';
wrapped = (new xml2js.Builder({
rootName: 'm2m:' + wrapType,
renderOpts: {pretty: false}
})).buildObject(message);
}
else {
wrapped = {};
wrapped[wrapType] = message;
}
return wrapped;
}
/**
*
*/
var nameLengthChanger = function (changer) {
var cat = {
rsc: ['ResourceTypes', 'PrimitiveRootElements'],
atr: ['PrimitiveParameters', 'ResourceAttributes', 'ComplexDataTypesMembers']
};
var process = function (src) {
if (isSimple(typeof src)) {
return src;
}
var dst;
if (Array.isArray(src)) {
dst = [];
for (var i = 0; i < src.length; ++i) {
dst.push(process(src[i]));
}
}
else {
dst = {};
for (var ii in src) {
var id = ii;
if (id !== 'address') try {
id = changer(ii, isSimple(typeof src[ii]) ? cat.atr : cat.rsc);
}
catch (e) {
try {
id = changer(ii, isSimple(typeof src[ii]) ? cat.rsc : cat.atr);
}
catch (e) {
// ignore not exsting jargon
log.error('cannot find jargon: ' + id);
}
}
dst[id] = process(src[ii]);
}
}
return dst;
}
return process;
};
var toLongName = nameLengthChanger(name.getLong);
var toShortName = nameLengthChanger(name.getShort);
/**
*
*/
var parseJson = function (str) {
var obj;
try {
obj = JSON.parse(str);
}
catch (err) {
log.debug(typeof str === 'string' ? str : str.toString());
log.debug(err);
}
return obj;
}
/**
*
*/
var meanchild = function (src) {
var dst = {};
for (var ii in src) {
if (ii !== 'child') {
dst[ii] = (typeof src[ii] === 'object') ? meanchild(src[ii]) : src[ii];
}
}
if (src.child && Array.isArray(src.child)) {
src.child.forEach((child) => {
child = meanchild(child);
for (var jj in child) {
if (!dst[jj]) {
dst[jj] = child[jj];
}
else {
if (!Array.isArray(dst[jj])) {
dst[jj] = [dst[jj]];
}
dst[jj].push(child[jj]);
}
}
});
}
return dst;
}
/**
* determine Resource Addressing Method
*/
var determineResourceAddressingMethod = function (addr) {
if (addr.match(/^\/[^\/]/)) return code.getResourceAddressingMethod('SP-Relative');
if (addr.match(/^\/\/[^\/]/)) return code.getResourceAddressingMethod('Absolute');
return code.getResourceAddressingMethod('CSE-Relative');
}
/**
* determine Resource Address type
*/
var determineResourceAddressingType = function (addr, cseid, spid) {
var ttt = getCSERelativeAddress(addr, cseid, spid).split('/');
return code.getDiscResType(isStructured(ttt) ? 'structured' : 'unstructured');
}
/**
*
*/
var getCSERelativeAddress = (addr, cseid, spid) => {
var ttt = addr.split('/');
var method = determineResourceAddressingMethod(addr);
if (method === code.getResourceAddressingMethod('CSE-Relative')) {
if (ttt[0] !== cseid) addr = cseid + '/' + addr;
return addr;
}
if (method === code.getResourceAddressingMethod('SP-Relative')) {
if (ttt[0].length === 0 && ttt[1] === cseid) {
return addr.replace(/^\//, '');
}
}
if (method === code.getResourceAddressingMethod('Absolute')) {
if (ttt[2] === spid && ttt[3] === cseid) {
return ttt.slice(3).join('/');
}
}
console.error('[//%s/%s] cannot handle: %s', spid, cseid, addr);
// throw new Error(addr);
return null;
}
/**
*
*/
var getSPRelativeAddress = (addr, cseid, spid) => {
var ttt = addr.split('/');
var method = determineResourceAddressingMethod(addr);
if (method === code.getResourceAddressingMethod('CSE-Relative')) {
if (ttt[0] !== cseid) addr = cseid + '/' + addr;
return '/' + addr;
}
if (method === code.getResourceAddressingMethod('SP-Relative')) {
if (ttt[0].length === 0 && ttt[1] === cseid) {
return addr;
}
}
if (method === code.getResourceAddressingMethod('Absolute')) {
if (ttt[2] === spid && ttt[3] === cseid) {
return '/' + ttt.slice(3).join('/');
}
}
console.error('[//%s/%s] cannot handle: %s', spid, cseid, addr);
// throw new Error(addr);
return null;
}
/**
*
*/
var getAbsoluteAddress = (addr, cseid, spid) => {
var ttt = addr.split('/');
var method = determineResourceAddressingMethod(addr);
if (method === code.getResourceAddressingMethod('CSE-Relative')) {
if (ttt[0] !== cseid) addr = cseid + '/' + addr;
return '//' + spid + '/' + addr;
}
if (method === code.getResourceAddressingMethod('SP-Relative')) {
if (ttt[0].length === 0 && ttt[1] === cseid) {
return '//' + spid + addr;
}
}
if (method === code.getResourceAddressingMethod('Absolute')) {
if (ttt[2] === spid && ttt[3] === cseid) {
return addr;
}
}
console.error('[//%s/%s] cannot handle: %s', spid, cseid, addr);
// throw new Error(addr);
return null;
}
/**
*
*/
var processData = function (data, callback) {
var parser = new xml2js.Parser({ explicitArray: false });
parser.parseString(data, function (err, result) {
var msg = undefined;
if (err) {
msg = parseJson(data);
if (!msg) {
log.error('json string syntax error has occurred while parsing request body');
}
for (var ii in msg) {
if (ii.match(/^m2m:/)) {
msg[ii.replace('m2m:', '')] = msg[ii];
delete msg[ii];
}
}
msg.cty = 'json';
}
else {
msg = {};
for (var ii in result) {
log.debug(ii);
if ('$' in result[ii]) {
var ttt = result[ii]['$'];
Object.keys(ttt).map((key) => {
if (key !== 'xmlns:m2m') {
result[ii][key.replace('^m2m', '')] = ttt[key];
}
})
delete result[ii]['$'];
}
msg[ii.replace('m2m:', '')] = result[ii];
}
msg.cty = 'xml';
}
// console.log(JSON.stringify(msg, null, ' '));
if (msg) msg = toShortName(msg);
if (callback) callback(msg);
});
};
/**
*
*/
var path2addr = (path) => {
// see clause 6.2.2.1 Path component (oneM2M TS-0009-V2.6.1)
if (path.indexOf('/~/') === 0) return path.replace('/~/', '/');
if (path.indexOf('/_/') === 0) return path.replace('/_/', '//');
if (path.indexOf('/') === 0) return path.replace('/', '');
return path;
}
/**
*
*/
var addr2path = (addr) => {
// see clause 6.2.2.1 Path component (oneM2M TS-0009-V2.6.1)
var pre = ((ram) => {
// if (ram === code.getResourceAddressingMethod('CSE-Relative')) return '/' + myID + '/';
if (ram === code.getResourceAddressingMethod('CSE-Relative')) return '/';
else if (ram === code.getResourceAddressingMethod('SP-Relative')) return '/~' ;
else if (ram === code.getResourceAddressingMethod('Absolute')) return '/_';
}) (determineResourceAddressingMethod(addr));
return pre + addr;
}
/**
* get mime type of oneM2M primitive
*/
var mime = (pri) => {
var ser = pri.cty || 'json';
if (pri.op && pri.op === code.getOperation('Notify')) {
dat = 'ntfy';
}
else {
dat = 'res';
}
return util.format('application/vnd.onem2m-%s+%s', dat, ser);
}
/**
*
*/
var log = (function () {
try {
return require('./logger').logger();
}
catch (e) {
return {
info: console.info,
debug: console.log,
warn: console.warn,
error: console.error,
};
}
})();
//
// utilities
//
exports.isID = isID;
exports.isStructured = isStructured;
exports.createRequestID = createRequestID;
exports.createResourceID = createResourceID;
exports.translateOperation = translateOperation;
exports.wrapMessage = wrapMessage;
exports.toLongName = toLongName;
exports.toShortName = toShortName;
exports.parseJson = parseJson;
exports.meanchild = meanchild;
exports.determineResourceAddressingMethod = determineResourceAddressingMethod;
exports.determineResourceAddressingType = determineResourceAddressingType;
exports.getCSERelativeAddress = getCSERelativeAddress;
exports.getSPRelativeAddress = getSPRelativeAddress;
exports.getAbsoluteAddress = getAbsoluteAddress;
exports.processData = processData;
exports.path2addr = path2addr;
exports.addr2path = addr2path;
exports.mime = mime;
exports.log = log;