node-red-contrib-snap4city-user
Version:
Nodes for Snap4city project, targeted to standard user (no developer)
378 lines (365 loc) • 15.9 kB
JavaScript
var fs = require('fs')
var path = require('path')
var os = require('os')
class SubscriptionStore {
constructor(RED, jsonFilePath = 'subscriptions.json') {
this.subscriptionJson = {};
let correctPath;
if (RED.settings.APPID != null) { // cloud / IoTApp scenario
correctPath = "/data/.snap4cityConfig";
} else { // local / edge scenario
correctPath = path.join(os.homedir(), ".snap4cityConfig");
}
this.jsonFilePath = path.join(correctPath, jsonFilePath);
if (!fs.existsSync(correctPath)) {
fs.mkdirSync(correctPath, { recursive: true });
fs.writeFileSync(this.jsonFilePath, JSON.stringify(this.subscriptionJson));
} else if (!fs.existsSync(this.jsonFilePath)) {
fs.writeFileSync(this.jsonFilePath, JSON.stringify(this.subscriptionJson));
} else {
this.subscriptionJson = JSON.parse(fs.readFileSync(this.jsonFilePath));
}
}
getSubscriptionOfNode(nodeId) {
return this.subscriptionJson[nodeId]
}
setSubscriptionOnNode(subscriptionId, nodeId) {
this.subscriptionJson[nodeId] = subscriptionId;
fs.writeFileSync(this.jsonFilePath, JSON.stringify(this.subscriptionJson));
}
}
class NodeStatus {
initializing(node) {
node.status({
fill: "blue",
shape: "dot",
text: "Initializing ..."
});
}
querying(node) {
node.status({
fill: "blue",
shape: "dot",
text: "... Querying ..."
});
}
sending(node) {
node.status({
fill: "blue",
shape: "dot",
text: "... Sending ..."
});
}
subscribing(node) {
node.status({
fill: "blue",
shape: "dot",
text: "... Subscribing ..."
});
}
success(node) {
node.status({
fill: "green",
shape: "dot",
text: "Success"
});
}
listening(node, url) {
node.status({
fill: "blue",
shape: "dot",
text: "Listening on: " + url
});
}
send(node, payload, statusCode) {
node.send({
payload: payload,
statusCode: statusCode
});
}
getError(node, err, msgLoggedOnDebug = null) {
node.status({
fill: "red",
shape: "ring",
text: this.getErrorMessage(err)
});
node.error(msgLoggedOnDebug ? msgLoggedOnDebug : err);
}
//reason is a json
getErrorMessage(reason) {
try {
if (JSON.stringify(reason).indexOf("TIMEDOUT") != -1)
return "Problem, Timeout";
else if (JSON.stringify(reason).indexOf("ENOTFOUND") != -1)
return "Problem, Not reachable";
else if (JSON.stringify(reason).indexOf("ECONNREFUSED") != -1)
return "Problem, Connection refused";
else if (JSON.stringify(reason).indexOf("SELF_SIGNED_CERT_IN_CHAIN") != -1)
return "Problem, CA certificate not valid";
else if (JSON.stringify(reason).indexOf("is not in the cert's list") != -1)
return "Problem, Broker URL mismatch";
else if ((JSON.stringify(reason).indexOf("certificate unknown") != -1) || (JSON.stringify(reason).indexOf("EPROTO") != -1))
return "Problem, Certificate credentials not valid";
else if (JSON.stringify(reason).indexOf("key values mismatch") != -1)
return "Problem, Key values mismatch";
else if (JSON.parse(reason.payload).message)
return "Problem: " + JSON.parse(reason.payload).message
else if (JSON.parse(reason.payload).description)
return "Problem: " + JSON.parse(reason.payload).description
} catch (err) {
return "Problem: " + reason.payload;
}
}
}
class OrionHttpRequestOptions {
setHeaderAuthTenantAndTls(options, config, RED, auth = null) {
var tlsNode = RED.nodes.getNode(config.tls);
var apikey = (auth && auth.apikey) ? auth.apikey : ((config.apikey && config.apikey != "") ? config.apikey : undefined);
var basicAuth = (auth && auth.basicAuth) ? auth.basicAuth : ((config.basicAuth && config.basicAuth != "") ? config.basicAuth : undefined);
if (apikey)
options.headers.apikey = config.apikey;
if (basicAuth)
options.headers.Authorization = config.basicAuth;
if (config.tenant != null && config.tenant != "")
options.headers["Fiware-Service"]=config.tenant;
if (config.servicepath != null && config.servicepath != "") {
if (!config.servicepath.startsWith("/"))
config.servicepath="/"+config.servicepath;
options.headers["Fiware-ServicePath"]=config.servicepath;
}
if (tlsNode != null)
if (tlsNode.credentials != null) {
options.key = tlsNode.credentials.keydata;
options.cert = tlsNode.credentials.certdata;
options.ca = tlsNode.credentials.cadata;
options.rejectUnauthorized = tlsNode.verifyservercert;
}
return options
}
generateForOrionAPIV2Update(hostname, port, prefixPath, config, auth, payloadLength, accessToken) {
var k1 = (auth && auth.k1) ? auth.k1 : ((config.userk1) ? config.userk1 : undefined);
var k2 = (auth && auth.k2) ? auth.k2 : ((config.passk2) ? config.passk2 : undefined);
var options = {
hostname: hostname,
port: port,
path: prefixPath + "/v2/entities/" + config.enid + "/attrs?type=" + config.entype + "&elementid=" + config.enid + (k1 ? "&k1=" + k1 : "") + (k2 ? "&k2=" + k2 : ""),
method: 'PATCH',
rejectUnauthorized: false,
headers: {
'Authorization': 'Bearer ' + accessToken,
'Content-Type': 'application/json',
'Content-Length': payloadLength
}
};
return options
}
generateForOrionAPIV2Query(hostname, port, prefixPath, config, queryParams, accessToken) {
var query = `${queryParams.entities[0].id}/?type=${queryParams.entities[0].type}`;
query += queryParams.attributes != "" ? `&attrs=${queryParams.attributes}` : '';
var options = {
hostname: hostname,
port: port,
path: prefixPath + "/v2/entities/" + query + "&limit=" + config.limit + "&elementid=" + config.enid + (config.userk1 ? "&k1=" + config.userk1 : "") + (config.passk2 ? "&k2=" + config.passk2 : ""),
method: 'GET',
rejectUnauthorized: false,
headers: {
'Authorization': 'Bearer ' + accessToken,
}
};
return options
}
generateForOrionAPIV2Subscribe(hostname, port, prefixPath, config, payloadLength, accessToken) {
var options = {
hostname: hostname,
port: port,
path: prefixPath + "/v2/subscriptions/?elementid=" + config.enid + (config.userk1 ? "&k1=" + config.userk1 : "") + (config.passk2 ? "&k2=" + config.passk2 : ""),
method: 'POST',
rejectUnauthorized: false,
headers: {
'Authorization': 'Bearer ' + accessToken,
'Content-Type': 'application/json',
'Accept': 'application/json',
'Content-Length': payloadLength
}
};
return options
}
generateForOrionAPIV2SubscribeExact(hostname, port, prefixPath, config, payloadLength, accessToken) {
var k1 = (config.userk1) ? config.userk1 : undefined;
var k2 = (config.passk2) ? config.passk2 : undefined;
var query = [];
if (k1) query.push("k1=" + encodeURIComponent(k1));
if (k2) query.push("k2=" + encodeURIComponent(k2));
var path = prefixPath + "/v2/subscriptions";
if (query.length > 0) {
path += "?" + query.join("&");
}
var options = {
hostname: hostname,
port: port,
path: path,
method: 'POST',
rejectUnauthorized: false,
headers: {
'Authorization': 'Bearer ' + accessToken,
'Content-Type': 'application/json',
'Accept': 'application/json',
'Content-Length': payloadLength
}
};
return options
}
generateForOrionAPIV2Unsubscribe(hostname, port, prefixPath, config, subscriptionId, accessToken) {
var options = {
hostname: hostname,
port: port,
path: prefixPath + "/v2/subscriptions/" + subscriptionId + "/?elementid=" + config.enid + (config.userk1 ? "&k1=" + config.userk1 : "") + (config.passk2 ? "&k2=" + config.passk2 : ""),
method: 'DELETE',
rejectUnauthorized: false,
headers: {
'Authorization': 'Bearer ' + accessToken,
}
};
return options
}
}
module.exports = {
getContextBrokerListForRegisterActivity: function (RED, node, contextBrokerUrl, contextBrokerPort, deviceName, iotappid, accessToken) {
if (typeof node.currentContextBroker != "undefined" && node.currentContextBroker.accesslink.indexOf(contextBrokerUrl) == -1) {
delete node.contextBrokerList;
}
if (typeof node.contextBrokerList == "undefined") {
var lista = module.exports.getContextBrokerList(RED, accessToken, node);
if (lista != null) {
node.contextBrokerList = lista;
module.exports.createDeviceLongIdAndRegisterActivity(RED, node, contextBrokerUrl, contextBrokerPort, deviceName, iotappid, accessToken);
}
} else {
module.exports.createDeviceLongIdAndRegisterActivity(RED, node, contextBrokerUrl, contextBrokerPort, deviceName, iotappid, accessToken);
}
},
getContextBrokerList: function (RED, accessToken, node){
var XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest;
var xmlHttp = new XMLHttpRequest();
var s4cUtility = require("./snap4city-utility.js");
node.s4cAuth = RED.nodes.getNode(node.service.authentication);
var uri = s4cUtility.settingUrl(RED,node, "iotDirectoryUrl", "https://www.snap4city.org", "/iot-directory/") + "api/";
if (accessToken != "" && typeof accessToken != "undefined") {
xmlHttp.open("GET", encodeURI(uri + "contextbroker.php?action=get_all_contextbroker&nodered=yes"), false);
xmlHttp.setRequestHeader("Content-Type", "application/json");
xmlHttp.setRequestHeader("Authorization", "Bearer " + accessToken);
xmlHttp.send(null);
if (xmlHttp.readyState === 4) {
if (xmlHttp.status === 200) {
if (xmlHttp.responseText != "") {
try {
return JSON.parse(xmlHttp.responseText).data;
} catch (err) {
return xmlHttp.responseText.data;
}
} else {
console.log("Error returned in getContextBrokerList (Empty response)");
return null;
}
} else {
console.log("Error returned in getContextBrokerList (1):" + xmlHttp.statusText);
return null;
}
} else {
console.log("Error returned in getContextBrokerList (2):" + xmlHttp.readyState);
return null;
}
} else {
console.log("Error returned in getContextBrokerList (Empty accessToken)");
return null;
}
},
createDeviceLongIdAndRegisterActivity: function (RED, node, contextBrokerUrl, contextBrokerPort, deviceName, iotappid, accessToken) {
var XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest;
var xmlHttp = new XMLHttpRequest();
var s4cUtility = require("./snap4city-utility.js");
if (typeof node.deviceLongId == "undefined" || node.deviceLongId.indexOf(deviceName) == -1) {
if (typeof node.contextBrokerList != "undefined") {
for (var i = 0; i < node.contextBrokerList.length; i++) {
if (node.contextBrokerList[i].accesslink.indexOf(contextBrokerUrl) != -1 && node.contextBrokerList[i].accessport == contextBrokerPort) {
node.deviceLongId = node.contextBrokerList[i].organization + ":" + node.contextBrokerList[i].name + ":" + deviceName;
node.currentContextBroker = node.contextBrokerList[i];
}
}
}
}
if (typeof node.deviceLongId != "undefined") {
node.s4cAuth = RED.nodes.getNode(node.service.authentication);
var uri = s4cUtility.settingUrl(RED,node, "myPersonalDataUrl", "https://www.snap4city.org", "/datamanager/api/v1/");
if (accessToken != "" && typeof accessToken != "undefined") {
xmlHttp.open("POST", encodeURI(uri + "lightactivities/?elementType=IOTID&sourceRequest=iotapp&sourceId=" + iotappid + "&elementId=" + node.deviceLongId), true);
xmlHttp.setRequestHeader("Content-Type", "application/json");
xmlHttp.setRequestHeader("Authorization", "Bearer " + accessToken);
xmlHttp.onload = function (e) {
if (xmlHttp.readyState === 4) {
if (xmlHttp.status !== 200) {
console.log("Error returned in createDeviceLongIdAndRegisterActivity (1):"+xmlHttp.status);
}
} else {
console.log("Error returned in createDeviceLongIdAndRegisterActivity (2):"+xmlHttp.readyState);
}
};
xmlHttp.onerror = function (e) {
console.log("Error returned in createDeviceLongIdAndRegisterActivity (3):"+e);
};
xmlHttp.send(null);
}
}
},
processInput: function (n, msg) {
n.url = n.url || msg.url;
n.port = n.port || msg.port;
n.enid = n.enid || msg.enid || ".*"; //TODO don't allow .* for queryContext (but also for subscribeContext)
n.entype = n.entype || msg.entype;
n.limit = n.limit || msg.limit || LIMIT;
n.userk1 = n.userk1 || msg.userk1;
n.passk2 = n.passk2 || msg.passk2;
n.tenant = n.tenant || msg.tenant;
n.servicepath = n.servicepath || msg.servicepath;
n.apikey = n.apikey || msg.apikey;
n.basicAuth = n.basicAuth || msg.basicAuth;
n.attributes = n.attributes || msg.attributes;
n.ispattern = n.ispattern || msg.ispattern || false;
n.includeattr = n.includeattr || msg.includeattr;
n.rtype = n.rtype || msg.rtype;
n.rvalue = n.rvalue || msg.rvalue;
if (n.rtype && !n.rvalue) {
n.rvalue = "entity::type";
}
// n.attributes = n.attributes || '.*';
n.attributes = n.attributes || [];
if (n.attributes.constructor !== Array) {
n.attributes = (n.attributes || "").split(",");
for (var i = 0; i < n.attributes.length; i++) {
n.attributes[i] = n.attributes[i].trim();
}
}
},
splitUrlInHostnameAndPrefixPath: function (url) {
var urlWithoutHttp = url;
if (urlWithoutHttp.indexOf("http") != -1)
urlWithoutHttp = urlWithoutHttp.replace("https://", "").replace("http://", "");
var prefixPath = "";
var hostname = urlWithoutHttp;
var indexSlash = urlWithoutHttp.indexOf("/");
var indexDot = urlWithoutHttp.indexOf(":");
if (indexSlash!=-1 || indexDot!=-1){
if (indexDot!=-1)
//here the port information is ignored (use orionBrokerService.port instead)
hostname = urlWithoutHttp.substring(0, indexDot);
else
hostname = urlWithoutHttp.substring(0, indexSlash);
if (indexSlash!=-1)
prefixPath = urlWithoutHttp.substring(indexSlash);
}
return [hostname, prefixPath];
},
SubscriptionStore: SubscriptionStore,
NodeStatus: NodeStatus,
OrionHttpRequestOptions: OrionHttpRequestOptions
}