UNPKG

@mindconnect/mindconnect-nodejs

Version:

NodeJS Library for Siemens Insights Hub Connectivity - TypeScript SDK for Insights Hub and Industrial IoT - Command Line Interface - Insights Hub Development Proxy (Siemens Insights Hub was formerly known as MindSphere)

369 lines 14.3 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.removeTrailingSlash = exports.printTree = exports.isGuid = exports.convertToDataPoints = exports.checkList = exports.addAndStoreConfiguration = exports.pruneCert = exports.checksumFile = exports.removeUndefined = exports.toQueryString = exports.throwError = exports.checkAssetId = exports.retry = exports.checkCertificate = exports.getConfigProfile = exports.getFullConfig = exports.loadAuth = exports.storeAuth = exports.getHomeDotMcDir = exports.getAgentDir = exports.decrypt = exports.encrypt = exports.getPiamUrl = exports.isUrl = exports.upgradeOldConfiguration = exports.convertToTdpArray = void 0; // Copyright (C), Siemens AG 2017 const crypto = require("crypto"); const fs = require("fs"); const os = require("os"); const url_1 = require("url"); const groupby = require("json-groupby"); const convertToTdpArray = (data) => { const tdpArray = []; const groupedData = groupby(data, ["timestamp"]); for (const element in groupedData) { groupedData[element].forEach((x) => { delete x["timestamp"]; }); const tdp = { timestamp: element, values: groupedData[element], }; tdpArray.push(tdp); } return tdpArray; }; exports.convertToTdpArray = convertToTdpArray; function upgradeOldConfiguration(obj) { if (obj.auth && obj.iv && obj.gateway && obj.tenant) { return { credentials: [ Object.assign(Object.assign({}, obj), { selected: true, type: "SERVICE", createdAt: new Date().toISOString(), appName: "", appVersion: "", usertenant: "" }), ], }; } return obj; } exports.upgradeOldConfiguration = upgradeOldConfiguration; const isUrl = (url) => { try { new url_1.URL(url); return true; } catch (e) { return false; } }; exports.isUrl = isUrl; const getPiamUrl = (gateway, tenant) => { const piamUrl = gateway.replace("gateway", `${tenant}.piam`); return piamUrl.endsWith("/") ? piamUrl : piamUrl + "/"; }; exports.getPiamUrl = getPiamUrl; const normalizePasskey = (passkey) => { return passkey.length < 32 ? passkey + new Array(33 - passkey.length).join("$") : passkey.substr(0, 32); }; const encrypt = ({ user, password, passkey, gateway, tenant, type, usertenant, appName, appVersion, createdAt, selected, }) => { const base64encoded = Buffer.from(`${user}:${password}`).toString("base64"); const iv = crypto.randomBytes(16); const cipher = crypto.createCipheriv("aes-256-ctr", Buffer.from(normalizePasskey(passkey)), iv); let crypted = cipher.update(`Basic ${base64encoded}`, "utf8", "hex"); crypted += cipher.final("hex"); const encryptedAuth = { auth: crypted.toString(), iv: iv.toString("base64"), gateway: gateway, tenant: tenant, type: type, usertenant: usertenant, appName: appName, appVersion: appVersion, createdAt: createdAt, selected: selected, }; // console.log(encryptedAuth); return encryptedAuth; }; exports.encrypt = encrypt; const decrypt = (encryptedAuth, passkey) => { const decipher = crypto.createDecipheriv("aes-256-ctr", normalizePasskey(passkey), Buffer.from(encryptedAuth.iv, "base64")); let dec = decipher.update(encryptedAuth.auth, "hex", "utf8"); dec += decipher.final("utf8"); return dec; }; exports.decrypt = decrypt; const getAgentDir = (path) => { let result; if (fs.existsSync(`${path}/.mc/`)) { result = `${path}/.mc/`; } else if (fs.existsSync(`${process.cwd()}/.mc/`)) { result = `${process.cwd()}/.mc/`; } else { result = (0, exports.getHomeDotMcDir)(); } return result; }; exports.getAgentDir = getAgentDir; const getHomeDotMcDir = () => { return `${os.homedir()}/.mc/`; }; exports.getHomeDotMcDir = getHomeDotMcDir; const storeAuth = (auth) => { const homeDir = (0, exports.getHomeDotMcDir)(); if (!fs.existsSync(homeDir)) { fs.mkdirSync(homeDir); } const pathName = `${(0, exports.getHomeDotMcDir)()}auth.json`; fs.writeFileSync(pathName, JSON.stringify(auth)); }; exports.storeAuth = storeAuth; const loadAuth = () => { const fullConfig = getFullConfig(); let result = undefined; for (let index = 0; index < fullConfig.credentials.length; index++) { const element = fullConfig.credentials[index]; if (element.selected) { result = element; break; } } !result && (0, exports.throwError)("please configure the authentication: https://developer.siemens.com/industrial-iot-open-source/mindconnect-nodejs/cli/setting-up-the-cli.html "); return result; }; exports.loadAuth = loadAuth; function getFullConfig() { const homeDir = (0, exports.getHomeDotMcDir)(); if (!fs.existsSync(homeDir)) { fs.mkdirSync(homeDir); console.log(`creating ${homeDir} folder`); } // create empty auth.json const pathName = `${(0, exports.getHomeDotMcDir)()}auth.json`; if (!fs.existsSync(pathName)) { fs.writeFileSync(pathName, JSON.stringify({ credentials: [] })); console.log(`initializing ${pathName} with empty configuration`); } const buffer = fs.readFileSync(pathName); let obj = JSON.parse(buffer.toString()); // console.log(obj); if (obj.auth && obj.iv && obj.gateway && obj.tenant) { const upgraded = upgradeOldConfiguration(obj); fs.writeFileSync(pathName, JSON.stringify(upgraded)); obj = upgraded; console.log("upgraded configuration to the new format"); } return obj; } exports.getFullConfig = getFullConfig; const getConfigProfile = (config) => { try { const result = `${config.content.clientCredentialProfile}`; if (["SHARED_SECRET", "RSA_3072"].indexOf(result) < 0) { throw new Error("Configuration profile not supported. The library only supports the shared_secret and RSA_3072 config profiles"); } return result; } catch (err) { throw new Error("Configuration profile not supported. The library only supports the shared_secret and RSA_3072 config profiles"); } }; exports.getConfigProfile = getConfigProfile; const checkCertificate = (config, options) => { const profile = (0, exports.getConfigProfile)(config); if (profile === "RSA_3072") { if (!options.cert) { throw new Error("You have to specify --cert parameter for RSA_3072 agents"); } if (!fs.existsSync(options.cert)) { throw new Error(`Can't find file ${options.cert}`); } } return profile === "RSA_3072"; }; exports.checkCertificate = checkCertificate; const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); /** * retry the function n times (while progressively waiting for the success) until success * the waiting schema is iteration * timeoutInMiliseconds (default is 300ms) * * @param {number} n * @param {Function} func * @param {number} [timoutinMilliseconds=300] * @param {Function} [logFunction] * @returns */ const retry = (n_1, func_1, ...args_1) => __awaiter(void 0, [n_1, func_1, ...args_1], void 0, function* (n, func, timoutinMilliseconds = 300, logFunction) { let error; for (let i = 0; i < n; i++) { try { if (logFunction) { logFunction(); } if (i > 0) { yield sleep(i * timoutinMilliseconds); } return yield func(); } catch (err) { error = err; } } throw error; }); exports.retry = retry; const checkAssetId = (agentId) => { if (!/[a-f0-9]{32}/gi.test(agentId)) { throw new Error("You have to pass valid 32 char long asset id"); } }; exports.checkAssetId = checkAssetId; const throwError = (error) => { throw new Error(error); }; exports.throwError = throwError; const toQueryString = (qs) => { return Object.keys(qs || {}) .filter((key) => { return qs[key] !== undefined; }) .map((key) => { const value = qs[key] instanceof Date ? qs[key].toISOString() : qs[key]; return encodeURIComponent(key) + "=" + encodeURIComponent(value); }) .join("&"); }; exports.toQueryString = toQueryString; const removeUndefined = (obj) => { Object.keys(obj).forEach((key) => obj[key] === undefined && delete obj[key]); return obj; }; exports.removeUndefined = removeUndefined; function checksumFile(hashName, path) { return __awaiter(this, void 0, void 0, function* () { return new Promise((resolve, reject) => { const hash = crypto.createHash(hashName); const stream = fs.createReadStream(path); stream.on("error", (err) => reject(err)); stream.on("data", (chunk) => hash.update(chunk)); stream.on("end", () => resolve(hash.digest("hex"))); }); }); } exports.checksumFile = checksumFile; function pruneCert(s) { return s .split(/\r\n|\r|\n/) .filter((x) => { return x.indexOf("CERTIFICATE") < 0; }) .join(""); } exports.pruneCert = pruneCert; function addAndStoreConfiguration(configuration) { const newConfiguration = { credentials: [], }; (!configuration || !configuration.credentials) && (0, exports.throwError)("invalid configuration!"); configuration.credentials.forEach((element) => { element.gateway = (0, exports.isUrl)(element.gateway) ? element.gateway : `https://gateway.${element.gateway}.mindsphere.io`; newConfiguration.credentials.push(element.passkey ? (0, exports.encrypt)(element) : element); }); checkList(newConfiguration.credentials); (0, exports.storeAuth)(newConfiguration); } exports.addAndStoreConfiguration = addAndStoreConfiguration; function checkList(list) { let count = 0; for (let i = 0; i < list.length; i++) { const element = list[i]; element.selected && count++; } if (count !== 1) { for (let i = 0; i < list.length; i++) { const element = list[i]; element.selected = i === 0; } } } exports.checkList = checkList; /** * Iterates over all properties of an object and returns it serialized as data points * * @export * @param {{ [x: string]: any }} obj Object to iterate over * @param {string} aspect aspect name in mindsphere * @param {((propertyName: string, aspect: string) => string | undefined)} dataPointFunction find id in the object * @param {((propertyName: string, aspect: string) => string | undefined)} qualityCodeFunction find quality code in the object * @param {(propertyName: string, aspect: string) => void} invalidDataFunction what to do if the data is not available * @returns */ function convertToDataPoints(obj, aspect, dataPointFunction, qualityCodeFunction, invalidDataFunction) { const res = []; function recurse(obj) { for (const propertyName in obj) { const value = obj[propertyName]; if (value) { if (Array.isArray(value)) { const dataPointId = dataPointFunction(propertyName, aspect); const qualityCode = qualityCodeFunction(propertyName, aspect); if (!dataPointId || !qualityCode) { invalidDataFunction(propertyName, aspect); } res.push({ dataPointId: `${dataPointId}`, qualityCode: `${qualityCode}`, value: `${JSON.stringify(value)}`, }); } else if (value && typeof value === "object") { recurse(value); } else { const dataPointId = dataPointFunction(propertyName, aspect); const qualityCode = qualityCodeFunction(propertyName, aspect); if (!dataPointId || !qualityCode) { invalidDataFunction(propertyName, aspect); } res.push({ dataPointId: `${dataPointId}`, qualityCode: `${qualityCode}`, value: `${value}`, }); } } } } recurse(obj); return res; } exports.convertToDataPoints = convertToDataPoints; function isGuid(x) { const guidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i; return guidRegex.test(x); } exports.isGuid = isGuid; function printTree(treeItem, level, color) { const prefix = level == 0 ? "" : "│ ".repeat(level) + "├─"; console.log(`${prefix}[${color(treeItem.data.assetId)}] ${treeItem.data.name} [${treeItem.data.typeId}]`); treeItem.children.forEach((child) => { printTree(child, level + 1, color); }); } exports.printTree = printTree; function removeTrailingSlash(url) { if (url.endsWith("/api/assetmanagement/v3/") || url.endsWith("/api/eventmanagement/v3/")) { return url; } // console.log(url); if (url.includes("?")) { const parts = url.split("?"); parts[0] = parts[0].replace(/\/+$/, ""); return parts.join("?"); } else { return url.replace(/\/+$/, ""); } } exports.removeTrailingSlash = removeTrailingSlash; //# sourceMappingURL=utils.js.map