Convert backend code to ES modules

Bundling code now with Rollup for its flat bundling and tree shaking
support. Front end code still goes through Webpack for browser support.
This commit is contained in:
Zaid Abdulla committed 2019-02-04 23:47:54 -08:00
1 parent 824639976f
commit 1e41c72d60
62 files changed
+711 -891

No files matched your search

+2 -1
View File
@@ -4,7 +4,8 @@
"node": true "node": true
}, },
"parserOptions": { "parserOptions": {
"ecmaVersion": 8 "ecmaVersion": 8,
"sourceType": "module"
}, },
"extends": "eslint:recommended", "extends": "eslint:recommended",
"rules": { "rules": {
+12 -12
View File
@@ -1,20 +1,20 @@
#!/usr/bin/env node #!/usr/bin/env -S node -r esm
"use strict"; "use strict";
const path = require("path"); import path from "path";
const fs = require("fs"); import fs from "fs";
process.chdir(path.resolve(path.dirname(fs.realpathSync(__filename)), "..")); process.chdir(path.resolve(path.dirname(fs.realpathSync(__filename)), ".."));
const config = require("../lib/config"); import * as config from "../lib/config";
const logger = require("../lib/logger"); import * as logger from "../lib/logger";
const cluster = require("../lib/cluster"); import * as cluster from "../lib/cluster";
const server = require("../lib/server"); import * as server from "../lib/server";
const cwmp = require("../lib/cwmp"); import * as cwmp from "../lib/cwmp";
const db = require("../lib/db"); import * as db from "../lib/db";
const extensions = require("../lib/extensions"); import * as extensions from "../lib/extensions";
const cache = require("../lib/cache"); import * as cache from "../lib/cache";
const VERSION = require("../package.json").version; import { version as VERSION } from "../package.json";
logger.init("cwmp", VERSION); logger.init("cwmp", VERSION);
+12 -19
View File
@@ -1,19 +1,18 @@
#!/usr/bin/env node #!/usr/bin/env -S node -r esm
"use strict"; "use strict";
const path = require("path"); import path from "path";
const fs = require("fs"); import fs from "fs";
process.chdir(path.resolve(path.dirname(fs.realpathSync(__filename)), "..")); process.chdir(path.resolve(path.dirname(fs.realpathSync(__filename)), ".."));
const config = require("../lib/config"); import * as config from "../lib/config";
const logger = require("../lib/logger"); import * as logger from "../lib/logger";
const cluster = require("../lib/cluster"); import * as cluster from "../lib/cluster";
const server = require("../lib/server"); import * as server from "../lib/server";
const _fs = require("../lib/fs"); import { listener } from "../lib/fs";
const db = require("../lib/db"); import * as db from "../lib/db";
const cache = require("../lib/cache"); import * as cache from "../lib/cache";
import { version as VERSION } from "../package.json";
const VERSION = require("../package.json").version;
logger.init("fs", VERSION); logger.init("fs", VERSION);
@@ -99,13 +98,7 @@ if (!cluster.worker) {
if (err) throw err; if (err) throw err;
cache.connect(err => { cache.connect(err => {
if (err) throw err; if (err) throw err;
server.start( server.start(SERVICE_PORT, SERVICE_ADDRESS, ssl, listener);
SERVICE_PORT,
SERVICE_ADDRESS,
ssl,
_fs.listener,
_fs.onConnection
);
}); });
}); });
} }
+13 -20
View File
@@ -1,20 +1,19 @@
#!/usr/bin/env node #!/usr/bin/env -S node -r esm
"use strict"; "use strict";
const path = require("path"); import path from "path";
const fs = require("fs"); import fs from "fs";
process.chdir(path.resolve(path.dirname(fs.realpathSync(__filename)), "..")); process.chdir(path.resolve(path.dirname(fs.realpathSync(__filename)), ".."));
const config = require("../lib/config"); import * as config from "../lib/config";
const logger = require("../lib/logger"); import * as logger from "../lib/logger";
const cluster = require("../lib/cluster"); import * as cluster from "../lib/cluster";
const server = require("../lib/server"); import * as server from "../lib/server";
const nbi = require("../lib/nbi"); import { listener } from "../lib/nbi";
const db = require("../lib/db"); import * as db from "../lib/db";
const extensions = require("../lib/extensions"); import * as extensions from "../lib/extensions";
const cache = require("../lib/cache"); import * as cache from "../lib/cache";
import { version as VERSION } from "../package.json";
const VERSION = require("../package.json").version;
logger.init("nbi", VERSION); logger.init("nbi", VERSION);
@@ -103,13 +102,7 @@ if (!cluster.worker) {
if (err) throw err; if (err) throw err;
cache.connect(err => { cache.connect(err => {
if (err) throw err; if (err) throw err;
server.start( server.start(SERVICE_PORT, SERVICE_ADDRESS, ssl, listener);
SERVICE_PORT,
SERVICE_ADDRESS,
ssl,
nbi.listener,
nbi.onConnection
);
}); });
}); });
} }
+15 -22
View File
@@ -1,20 +1,19 @@
#!/usr/bin/env node #!/usr/bin/env -S node -r esm
"use strict"; "use strict";
const path = require("path"); import * as path from "path";
const fs = require("fs"); import * as fs from "fs";
process.chdir(path.resolve(path.dirname(fs.realpathSync(__filename)), "..")); process.chdir(path.resolve(path.dirname(fs.realpathSync(__filename)), ".."));
const config = require("../lib/config"); import * as config from "../lib/config";
const logger = require("../lib/logger"); import * as logger from "../lib/logger";
const cluster = require("../lib/cluster"); import * as cluster from "../lib/cluster";
const server = require("../lib/server"); import * as server from "../lib/server";
const ui = require("../lib/ui"); import { listener } from "../lib/ui";
const db = require("../lib/ui/db"); import * as db from "../lib/ui/db";
const db2 = require("../lib/db"); import * as db2 from "../lib/db";
const cache = require("../lib/cache"); import * as cache from "../lib/cache";
import { version as VERSION } from "../package.json";
const VERSION = require("../package.json").version;
logger.init("ui", VERSION); logger.init("ui", VERSION);
@@ -102,22 +101,16 @@ if (!cluster.worker) {
.catch(exitWorkerUngracefully); .catch(exitWorkerUngracefully);
}); });
const listener = (req, res) => { const _listener = (req, res) => {
if (stopping) res.setHeader("Connection", "close"); if (stopping) res.setHeader("Connection", "close");
ui.listener(req, res); listener(req, res);
}; };
db2.connect(err => { db2.connect(err => {
if (err) throw err; if (err) throw err;
cache.connect(err => { cache.connect(err => {
if (err) throw err; if (err) throw err;
server.start( server.start(SERVICE_PORT, SERVICE_ADDRESS, ssl, _listener);
SERVICE_PORT,
SERVICE_ADDRESS,
ssl,
listener,
ui.onConnection
);
}); });
}); });
} }
+150 -81
View File
@@ -1,8 +1,11 @@
const path = require("path"); const path = require("path");
const fs = require("fs"); const fs = require("fs");
const util = require("util"); const { promisify } = require("util");
const { rollup } = require("rollup");
const rollupReplace = require("rollup-plugin-replace");
const rollupJson = require("rollup-plugin-json");
const { terser } = require("rollup-plugin-terser");
const webpack = require("webpack"); const webpack = require("webpack");
const nodeExternals = require("webpack-node-externals");
const postcss = require("postcss"); const postcss = require("postcss");
const postcssImport = require("postcss-import"); const postcssImport = require("postcss-import");
const postcssCssNext = require("postcss-cssnext"); const postcssCssNext = require("postcss-cssnext");
@@ -18,6 +21,37 @@ const BUILD_METADATA = new Date()
const INPUT_DIR = path.resolve(__dirname, ".."); const INPUT_DIR = path.resolve(__dirname, "..");
const OUTPUT_DIR = path.resolve(__dirname, "../dist"); const OUTPUT_DIR = path.resolve(__dirname, "../dist");
const externals = [
"path",
"fs",
"cluster",
"os",
"http",
"https",
"zlib",
"crypto",
"mongodb",
"libxmljs",
"vm",
"later",
"parsimmon",
"seedrandom",
"querystring",
"child_process",
"dgram",
"url",
"koa",
"koa-router",
"koa-compress",
"koa-bodyparser",
"koa-jwt",
"koa-static",
"jsonwebtoken",
"stream",
"mithril",
"parsimmon"
];
function rmDirSync(dirPath) { function rmDirSync(dirPath) {
if (!fs.existsSync(dirPath)) return; if (!fs.existsSync(dirPath)) return;
const files = fs.readdirSync(dirPath); const files = fs.readdirSync(dirPath);
@@ -30,22 +64,6 @@ function rmDirSync(dirPath) {
fs.rmdirSync(dirPath); fs.rmdirSync(dirPath);
} }
function cpDirSync(src, dst) {
fs.mkdirSync(dst);
const files = fs.readdirSync(src);
for (const file of files) {
const current = fs.lstatSync(path.join(src, file));
if (current.isDirectory()) {
cpDirSync(path.join(src, file), path.join(dst, file));
} else if (current.isSymbolicLink()) {
const symlink = fs.readlinkSync(path.join(src, file));
fs.symlinkSync(symlink, path.join(dst, file));
} else {
fs.copyFileSync(path.join(src, file), path.join(dst, file));
}
}
}
async function init() { async function init() {
// Delete any old output directory // Delete any old output directory
rmDirSync(OUTPUT_DIR); rmDirSync(OUTPUT_DIR);
@@ -56,6 +74,7 @@ async function init() {
fs.mkdirSync(OUTPUT_DIR + "/config"); fs.mkdirSync(OUTPUT_DIR + "/config");
fs.mkdirSync(OUTPUT_DIR + "/debug"); fs.mkdirSync(OUTPUT_DIR + "/debug");
fs.mkdirSync(OUTPUT_DIR + "/public"); fs.mkdirSync(OUTPUT_DIR + "/public");
fs.mkdirSync(OUTPUT_DIR + "/tools");
// Create package.json // Create package.json
const packageJson = require("../package.json"); const packageJson = require("../package.json");
@@ -90,11 +109,6 @@ async function copyStatic() {
path.resolve(OUTPUT_DIR, file) path.resolve(OUTPUT_DIR, file)
); );
} }
cpDirSync(
path.resolve(INPUT_DIR, "tools"),
path.resolve(OUTPUT_DIR, "tools")
);
} }
async function generateCss() { async function generateCss() {
@@ -109,82 +123,137 @@ async function generateCss() {
fs.writeFileSync(cssOutPath, cssOut.css); fs.writeFileSync(cssOutPath, cssOut.css);
} }
async function generateJs() { async function generateToolsJs() {
const backendConf = { for (const bin of ["configure-ui", "dump-data-model"]) {
mode: MODE, const inputFile = path.resolve(INPUT_DIR, `tools/${bin}`);
target: "node", const outputFile = path.resolve(OUTPUT_DIR, `tools/${bin}`);
node: false, const bundle = await rollup({
entry: { input: inputFile,
"genieacs-cwmp": path.resolve(INPUT_DIR, "bin/genieacs-cwmp"), external: externals,
"genieacs-nbi": path.resolve(INPUT_DIR, "bin/genieacs-nbi"), acorn: {
"genieacs-fs": path.resolve(INPUT_DIR, "bin/genieacs-fs"), allowHashBang: true
"genieacs-ui": path.resolve(INPUT_DIR, "bin/genieacs-ui")
},
resolve: {
alias: {
[path.resolve(INPUT_DIR, "package.json")]: path.resolve(
OUTPUT_DIR,
"package.json"
)
}
},
externals: [nodeExternals()],
output: {
path: path.resolve(OUTPUT_DIR, "bin")
},
module: {
rules: [
{
include: path.resolve(INPUT_DIR, "bin"),
use: path.join(__dirname, "./shebang-loader")
}
]
}, },
plugins: [ plugins: [
new webpack.BannerPlugin({ banner: "#!/usr/bin/env node", raw: true }) rollupReplace({
delimiters: ["", ""],
"#!/usr/bin/env -S node -r esm": ""
}),
MODE === "production" ? terser() : null
] ]
}; });
const frontendConf = { await bundle.write({
mode: MODE, format: "cjs",
entry: path.resolve(INPUT_DIR, "ui/app.js"), preferConst: true,
output: { banner: "#!/usr/bin/env node",
path: path.resolve(OUTPUT_DIR, "public"), file: outputFile
filename: "app.js" });
},
resolve: { // Mark as executable
alias: { const mode = fs.statSync(outputFile).mode;
[path.resolve(INPUT_DIR, "package.json")]: path.resolve( fs.chmodSync(outputFile, mode | 73);
OUTPUT_DIR,
"package.json"
)
} }
} }
};
const stats = await util.promisify(webpack)([backendConf, frontendConf]); async function generateBackendJs() {
for (const bin of [
// Remove js ext and mark as executable
for (const file of [
"genieacs-cwmp", "genieacs-cwmp",
"genieacs-nbi", "genieacs-nbi",
"genieacs-fs", "genieacs-fs",
"genieacs-ui" "genieacs-ui"
]) { ]) {
fs.renameSync( const inputFile = path.resolve(INPUT_DIR, `bin/${bin}`);
path.resolve(OUTPUT_DIR, `bin/${file}.js`), const outputFile = path.resolve(OUTPUT_DIR, `bin/${bin}`);
path.resolve(OUTPUT_DIR, `bin/${file}`) const bundle = await rollup({
); input: inputFile,
const mode = fs.statSync(path.resolve(OUTPUT_DIR, `bin/${file}`)).mode; external: externals,
fs.chmodSync(path.resolve(OUTPUT_DIR, `bin/${file}`), mode | 73); acorn: {
allowHashBang: true
},
treeshake: {
propertyReadSideEffects: false,
pureExternalModules: true
},
plugins: [
rollupReplace({
delimiters: ["", ""],
"#!/usr/bin/env -S node -r esm": ""
}),
rollupJson({ preferConst: true }),
{
resolveId: (importee, importer) => {
if (importee.endsWith("/package.json")) {
const p = path.resolve(path.dirname(importer), importee);
if (p === path.resolve(INPUT_DIR, "package.json"))
return path.resolve(OUTPUT_DIR, "package.json");
} }
return null;
}
},
MODE === "production" ? terser() : null
]
});
await bundle.write({
format: "cjs",
preferConst: true,
banner: "#!/usr/bin/env node",
file: outputFile
});
// Mark as executable
const mode = fs.statSync(outputFile).mode;
fs.chmodSync(outputFile, mode | 73);
}
}
async function generateFrontendJs() {
const inputFile = path.resolve(INPUT_DIR, "ui/app.js");
const outputFile = path.resolve(OUTPUT_DIR, "public/app.js");
const bundle = await rollup({
input: inputFile,
external: externals,
plugins: [rollupJson({ preferConst: true })],
inlineDynamicImports: true,
treeshake: {
propertyReadSideEffects: false,
pureExternalModules: true
},
onwarn: (warning, warn) => {
// Ignore circular dependency warnings
if (warning.code !== "CIRCULAR_DEPENDENCY") warn(warning);
}
});
await bundle.write({
preferConst: true,
format: "esm",
file: outputFile
});
const webpackConf = {
mode: MODE,
entry: outputFile,
output: {
path: path.resolve(OUTPUT_DIR, "public"),
filename: "app.js"
}
};
const stats = await promisify(webpack)(webpackConf);
process.stdout.write(stats.toString({ colors: true }) + "\n"); process.stdout.write(stats.toString({ colors: true }) + "\n");
} }
init() init()
.then(() => { .then(() => {
Promise.all([copyStatic(), generateCss(), generateJs()]) Promise.all([
copyStatic(),
generateCss(),
generateToolsJs(),
generateBackendJs(),
generateFrontendJs()
])
.then(() => {}) .then(() => {})
.catch(err => { .catch(err => {
process.stderr.write(err.stack + "\n"); process.stderr.write(err.stack + "\n");
-3
View File
@@ -1,3 +0,0 @@
module.exports = function(source) {
return source.replace(/^#![^\n\r]*[\r\n]/, "");
};
+13 -18
View File
@@ -18,15 +18,15 @@
*/ */
"use strict"; "use strict";
const crypto = require("crypto"); import crypto from "crypto";
const dgram = require("dgram"); import dgram from "dgram";
const URL = require("url"); import URL from "url";
const http = require("http"); import http from "http";
const config = require("./config"); import * as config from "./config";
const db = require("./db"); import * as db from "./db";
const common = require("./common"); import * as common from "./common";
const auth = require("./auth"); import * as auth from "./auth";
const cache = require("./cache"); import * as cache from "./cache";
function udpConReq(address, un, key, callback) { function udpConReq(address, un, key, callback) {
if (!address) return false; if (!address) return false;
@@ -187,7 +187,7 @@ function httpConReq(
}); });
} }
function connectionRequest(deviceId, callback) { export function connectionRequest(deviceId, callback) {
const CONNECTION_REQUEST_TIMEOUT = config.get( const CONNECTION_REQUEST_TIMEOUT = config.get(
"CONNECTION_REQUEST_TIMEOUT", "CONNECTION_REQUEST_TIMEOUT",
deviceId deviceId
@@ -284,7 +284,7 @@ function connectionRequest(deviceId, callback) {
}); });
} }
function watchTask(deviceId, taskId, timeout, callback) { export function watchTask(deviceId, taskId, timeout, callback) {
setTimeout(() => { setTimeout(() => {
db.tasksCollection.findOne( db.tasksCollection.findOne(
{ _id: taskId }, { _id: taskId },
@@ -324,7 +324,7 @@ function sanitizeTask(task, callback) {
callback(task); callback(task);
} }
function insertTasks(tasks, callback) { export function insertTasks(tasks, callback) {
if (tasks && common.typeOf(tasks) !== common.ARRAY_TYPE) tasks = [tasks]; if (tasks && common.typeOf(tasks) !== common.ARRAY_TYPE) tasks = [tasks];
else if (!tasks || tasks.length === 0) else if (!tasks || tasks.length === 0)
return void callback(null, tasks || []); return void callback(null, tasks || []);
@@ -344,7 +344,7 @@ function insertTasks(tasks, callback) {
} }
} }
function deleteDevice(deviceId, callback) { export function deleteDevice(deviceId, callback) {
db.tasksCollection.deleteMany({ device: deviceId }, err => { db.tasksCollection.deleteMany({ device: deviceId }, err => {
if (err) return void callback(err); if (err) return void callback(err);
db.devicesCollection.deleteOne({ _id: deviceId }, err => { db.devicesCollection.deleteOne({ _id: deviceId }, err => {
@@ -373,8 +373,3 @@ function deleteDevice(deviceId, callback) {
}); });
}); });
} }
exports.connectionRequest = connectionRequest;
exports.watchTask = watchTask;
exports.insertTasks = insertTasks;
exports.deleteDevice = deleteDevice;
+7 -11
View File
@@ -18,9 +18,9 @@
*/ */
"use strict"; "use strict";
const crypto = require("crypto"); import { createHash } from "crypto";
function parseAuthHeader(authHeader) { export function parseAuthHeader(authHeader) {
authHeader = authHeader.trim(); authHeader = authHeader.trim();
const method = authHeader.split(" ", 1)[0]; const method = authHeader.split(" ", 1)[0];
const res = { method: method }; const res = { method: method };
@@ -53,11 +53,11 @@ function parseAuthHeader(authHeader) {
return res; return res;
} }
function basic(username, password) { export function basic(username, password) {
return "Basic " + Buffer.from(`${username}:${password}`).toString("base64"); return "Basic " + Buffer.from(`${username}:${password}`).toString("base64");
} }
function digest(username, password, uri, httpMethod, body, authHeader) { export function digest(username, password, uri, httpMethod, body, authHeader) {
const cnonce = "0a4f113b"; const cnonce = "0a4f113b";
const nc = "00000001"; const nc = "00000001";
let qop; let qop;
@@ -68,7 +68,7 @@ function digest(username, password, uri, httpMethod, body, authHeader) {
else qop = authHeader.qop; else qop = authHeader.qop;
} }
let ha1 = crypto.createHash("md5"); let ha1 = createHash("md5");
ha1 ha1
.update(username) .update(username)
.update(":") .update(":")
@@ -78,7 +78,7 @@ function digest(username, password, uri, httpMethod, body, authHeader) {
// TODO support "MD5-sess" algorithm directive // TODO support "MD5-sess" algorithm directive
ha1 = ha1.digest("hex"); ha1 = ha1.digest("hex");
let ha2 = crypto.createHash("md5"); let ha2 = createHash("md5");
ha2 ha2
.update(httpMethod) .update(httpMethod)
.update(":") .update(":")
@@ -86,7 +86,7 @@ function digest(username, password, uri, httpMethod, body, authHeader) {
if (qop === "auth-int") ha2.update(":").update(body); if (qop === "auth-int") ha2.update(":").update(body);
ha2 = ha2.digest("hex"); ha2 = ha2.digest("hex");
const hash = crypto.createHash("md5"); const hash = createHash("md5");
hash hash
.update(ha1) .update(ha1)
.update(":") .update(":")
@@ -113,7 +113,3 @@ function digest(username, password, uri, httpMethod, body, authHeader) {
return authString; return authString;
} }
exports.parseAuthHeader = parseAuthHeader;
exports.basic = basic;
exports.digest = digest;
+10 -18
View File
@@ -18,7 +18,8 @@
*/ */
"use strict"; "use strict";
const config = require("./config"); import { MongoClient } from "mongodb";
import * as config from "./config";
const MAX_CACHE_TTL = config.get("MAX_CACHE_TTL"); const MAX_CACHE_TTL = config.get("MAX_CACHE_TTL");
@@ -26,11 +27,10 @@ let mongoClient;
let mongoCollection; let mongoCollection;
let mongoTimeOffset = 0; let mongoTimeOffset = 0;
function connect(callback) { export function connect(callback) {
const mongodb = require("mongodb");
const MONGODB_CONNECTION_URL = config.get("MONGODB_CONNECTION_URL"); const MONGODB_CONNECTION_URL = config.get("MONGODB_CONNECTION_URL");
mongodb.MongoClient.connect( MongoClient.connect(
MONGODB_CONNECTION_URL, MONGODB_CONNECTION_URL,
{ useNewUrlParser: true }, { useNewUrlParser: true },
(err, client) => { (err, client) => {
@@ -51,11 +51,11 @@ function connect(callback) {
); );
} }
function disconnect() { export function disconnect() {
if (mongoClient) mongoClient.close(); if (mongoClient) mongoClient.close();
} }
function get(key, callback) { export function get(key, callback) {
const expire = new Date(Date.now() - mongoTimeOffset); const expire = new Date(Date.now() - mongoTimeOffset);
if (Array.isArray(key)) { if (Array.isArray(key)) {
mongoCollection.find({ _id: { $in: key } }).toArray((err, res) => { mongoCollection.find({ _id: { $in: key } }).toArray((err, res) => {
@@ -83,13 +83,13 @@ function get(key, callback) {
} }
} }
function del(key, callback) { export function del(key, callback) {
if (Array.isArray(key)) if (Array.isArray(key))
mongoCollection.deleteMany({ _id: { $in: key } }, callback); mongoCollection.deleteMany({ _id: { $in: key } }, callback);
else mongoCollection.deleteOne({ _id: key }, callback); else mongoCollection.deleteOne({ _id: key }, callback);
} }
function set(key, value, ttl, callback) { export function set(key, value, ttl, callback) {
if (!callback && typeof v === "function") { if (!callback && typeof v === "function") {
callback = ttl; callback = ttl;
ttl = null; ttl = null;
@@ -106,7 +106,7 @@ function set(key, value, ttl, callback) {
); );
} }
function pop(key, callback) { export function pop(key, callback) {
mongoCollection.findOneAndDelete({ _id: key }, (err, res) => { mongoCollection.findOneAndDelete({ _id: key }, (err, res) => {
if (err || !res["value"]) return void callback(err); if (err || !res["value"]) return void callback(err);
@@ -117,7 +117,7 @@ function pop(key, callback) {
}); });
} }
function lock(lockName, ttl, callback) { export function lock(lockName, ttl, callback) {
const token = Math.random() const token = Math.random()
.toString(36) .toString(36)
.slice(2); .slice(2);
@@ -159,11 +159,3 @@ function lock(lockName, ttl, callback) {
} }
); );
} }
exports.connect = connect;
exports.disconnect = disconnect;
exports.get = get;
exports.set = set;
exports.del = del;
exports.pop = pop;
exports.lock = lock;
+7 -8
View File
@@ -18,8 +18,9 @@
*/ */
"use strict"; "use strict";
const cluster = require("cluster"); import cluster from "cluster";
const logger = require("./logger"); import { cpus } from "os";
import * as logger from "./logger";
let respawnTimestamp = 0; let respawnTimestamp = 0;
let tooManyCrashesTimestamp = 0; let tooManyCrashesTimestamp = 0;
@@ -62,7 +63,7 @@ function restartWorker(worker, code, signal) {
}, respawnTimestamp - now); }, respawnTimestamp - now);
} }
function start(workerCount, servicePort, serviceAddress) { export function start(workerCount, servicePort, serviceAddress) {
cluster.on("listening", (worker, address) => { cluster.on("listening", (worker, address) => {
if ( if (
(address.addressType === 4 || address.addressType === 6) && (address.addressType === 4 || address.addressType === 6) &&
@@ -80,16 +81,14 @@ function start(workerCount, servicePort, serviceAddress) {
cluster.on("exit", restartWorker); cluster.on("exit", restartWorker);
if (!workerCount) workerCount = Math.max(2, require("os").cpus().length); if (!workerCount) workerCount = Math.max(2, cpus().length);
for (let i = 0; i < workerCount; ++i) cluster.fork(); for (let i = 0; i < workerCount; ++i) cluster.fork();
} }
function stop() { export function stop() {
cluster.removeListener("exit", restartWorker); cluster.removeListener("exit", restartWorker);
for (const pid in cluster.workers) cluster.workers[pid].kill(); for (const pid in cluster.workers) cluster.workers[pid].kill();
} }
exports.start = start; export const worker = cluster.worker;
exports.stop = stop;
exports.worker = cluster.worker;
+15 -31
View File
@@ -18,19 +18,19 @@
*/ */
"use strict"; "use strict";
const UNDEFINED_TYPE = "[object Undefined]"; export const UNDEFINED_TYPE = "[object Undefined]";
const NULL_TYPE = "[object Null]"; export const NULL_TYPE = "[object Null]";
const BOOLEAN_TYPE = "[object Boolean]"; export const BOOLEAN_TYPE = "[object Boolean]";
const NUMBER_TYPE = "[object Number]"; export const NUMBER_TYPE = "[object Number]";
const STRING_TYPE = "[object String]"; export const STRING_TYPE = "[object String]";
const ARRAY_TYPE = "[object Array]"; export const ARRAY_TYPE = "[object Array]";
const OBJECT_TYPE = "[object Object]"; export const OBJECT_TYPE = "[object Object]";
const REGEXP_TYPE = "[object RegExp]"; export const REGEXP_TYPE = "[object RegExp]";
const DATE_TYPE = "[object Date]"; export const DATE_TYPE = "[object Date]";
const typeOf = obj => Object.prototype.toString.call(obj); export const typeOf = obj => Object.prototype.toString.call(obj);
function generateDeviceId(deviceIdStruct) { export function generateDeviceId(deviceIdStruct) {
// Escapes everything except alphanumerics and underscore // Escapes everything except alphanumerics and underscore
function esc(str) { function esc(str) {
return str.replace(/[^A-Za-z0-9_]/g, chr => { return str.replace(/[^A-Za-z0-9_]/g, chr => {
@@ -54,7 +54,7 @@ function generateDeviceId(deviceIdStruct) {
return esc(deviceIdStruct["OUI"]) + "-" + esc(deviceIdStruct["SerialNumber"]); return esc(deviceIdStruct["OUI"]) + "-" + esc(deviceIdStruct["SerialNumber"]);
} }
function parseAlias(pattern, start, res) { export function parseAlias(pattern, start, res) {
const aliases = []; const aliases = [];
let i = start; let i = start;
while (i < pattern.length && pattern[i] !== "]") { while (i < pattern.length && pattern[i] !== "]") {
@@ -126,7 +126,7 @@ function parseAlias(pattern, start, res) {
return i; return i;
} }
function parsePath(pattern, start, res) { export function parsePath(pattern, start, res) {
const path = []; const path = [];
path.wildcard = 0; path.wildcard = 0;
path.alias = 0; path.alias = 0;
@@ -163,7 +163,7 @@ function parsePath(pattern, start, res) {
return i; return i;
} }
function addPathMeta(path) { export function addPathMeta(path) {
if (path.alias != null && path.wildcard != null) return path; if (path.alias != null && path.wildcard != null) return path;
path.alias = 0; path.alias = 0;
@@ -183,22 +183,6 @@ function addPathMeta(path) {
} }
// Source: http://stackoverflow.com/a/6969486 // Source: http://stackoverflow.com/a/6969486
function escapeRegExp(str) { export function escapeRegExp(str) {
return str.replace(/[-[\]/{}()*+?.\\^$|]/g, "\\$&"); return str.replace(/[-[\]/{}()*+?.\\^$|]/g, "\\$&");
} }
exports.UNDEFINED_TYPE = UNDEFINED_TYPE;
exports.NULL_TYPE = NULL_TYPE;
exports.BOOLEAN_TYPE = BOOLEAN_TYPE;
exports.NUMBER_TYPE = NUMBER_TYPE;
exports.STRING_TYPE = STRING_TYPE;
exports.ARRAY_TYPE = ARRAY_TYPE;
exports.OBJECT_TYPE = OBJECT_TYPE;
exports.DATE_TYPE = DATE_TYPE;
exports.REGEXP_TYPE = REGEXP_TYPE;
exports.typeOf = typeOf;
exports.generateDeviceId = generateDeviceId;
exports.parsePath = parsePath;
exports.addPathMeta = addPathMeta;
exports.escapeRegExp = escapeRegExp;
+5 -7
View File
@@ -1,11 +1,11 @@
"use strict"; "use strict";
const validators = { import pass from "./validators/pass";
pass: require("./validators/pass"), import test from "./validators/test";
test: require("./validators/test")
};
class Authorizer { const validators = { pass, test };
export default class Authorizer {
constructor(permissionSets) { constructor(permissionSets) {
this.permissionSets = permissionSets; this.permissionSets = permissionSets;
this.validatorCache = new WeakMap(); this.validatorCache = new WeakMap();
@@ -77,5 +77,3 @@ class Authorizer {
return validator; return validator;
} }
} }
module.exports = Authorizer;
+4 -9
View File
@@ -1,6 +1,6 @@
"use strict"; "use strict";
const expressionParser = require("./expression-parser"); import { map, parseLikePattern } from "./expression-parser";
function or(cnf1, cnf2) { function or(cnf1, cnf2) {
if (cnf1.length === 0) return cnf2; if (cnf1.length === 0) return cnf2;
@@ -19,10 +19,10 @@ function or(cnf1, cnf2) {
return res; return res;
} }
function booleanCnf(exp) { export function booleanCnf(exp) {
const ranges = new Map(); const ranges = new Map();
const likePatterns = new Map(); const likePatterns = new Map();
expressionParser.map(exp, e => { map(exp, e => {
if (!Array.isArray(e)) return e; if (!Array.isArray(e)) return e;
if (e[0] === "LIKE" || e[0] === "NOT LIKE") { if (e[0] === "LIKE" || e[0] === "NOT LIKE") {
if ( if (
@@ -32,10 +32,7 @@ function booleanCnf(exp) {
const key = JSON.stringify(e[1]); const key = JSON.stringify(e[1]);
let s = likePatterns.get(key); let s = likePatterns.get(key);
if (!s) likePatterns.set(key, (s = new Map())); if (!s) likePatterns.set(key, (s = new Map()));
s.set( s.set(`${e[3] || ""}:${e[2]}`, parseLikePattern(e[2], e[3]));
`${e[3] || ""}:${e[2]}`,
expressionParser.parseLikePattern(e[2], e[3])
);
} }
} else if ([">", ">=", "<", "<=", "=", "<>"].includes(e[0])) { } else if ([">", ">=", "<", "<=", "=", "<>"].includes(e[0])) {
if (!Array.isArray(e[2])) { if (!Array.isArray(e[2])) {
@@ -251,5 +248,3 @@ function likePatternIncludes(pat1, pat2, idx1 = 0, idx2 = 0) {
if (idx1 === pat1.length && idx2 === pat2.length) return true; if (idx1 === pat1.length && idx2 === pat2.length) return true;
return false; return false;
} }
exports.booleanCnf = booleanCnf;
+5 -10
View File
@@ -1,6 +1,6 @@
"use strict"; "use strict";
const parsimmon = require("parsimmon"); import parsimmon from "parsimmon";
// Turn escaped characters into real ones (e.g. "\\n" becomes "\n"). // Turn escaped characters into real ones (e.g. "\\n" becomes "\n").
function interpretEscapes(str) { function interpretEscapes(str) {
@@ -22,7 +22,7 @@ function interpretEscapes(str) {
}); });
} }
function map(exp, callback) { export function map(exp, callback) {
if (!Array.isArray(exp)) return callback(exp); if (!Array.isArray(exp)) return callback(exp);
let clone; let clone;
@@ -271,12 +271,12 @@ const lang = parsimmon.createLanguage({
} }
}); });
function parse(str) { export function parse(str) {
if (!str) return null; if (!str) return null;
return lang.Expression.tryParse(str); return lang.Expression.tryParse(str);
} }
function stringify(exp, level = 0) { export function stringify(exp, level = 0) {
if (!Array.isArray(exp)) return JSON.stringify(exp); if (!Array.isArray(exp)) return JSON.stringify(exp);
const opLevels = { const opLevels = {
@@ -360,7 +360,7 @@ function stringify(exp, level = 0) {
} }
} }
function parseLikePattern(pat, esc) { export function parseLikePattern(pat, esc) {
const chars = pat.split(""); const chars = pat.split("");
for (let i = 0; i < chars.length; ++i) { for (let i = 0; i < chars.length; ++i) {
@@ -377,8 +377,3 @@ function parseLikePattern(pat, esc) {
} }
return chars.filter(c => c); return chars.filter(c => c);
} }
exports.parse = parse;
exports.stringify = stringify;
exports.map = map;
exports.parseLikePattern = parseLikePattern;
+15 -25
View File
@@ -1,8 +1,8 @@
"use strict"; "use strict";
const expressionParser = require("./expression-parser"); import { map, parseLikePattern } from "./expression-parser";
const expressionCnf = require("./expression-cnf"); import { booleanCnf } from "./expression-cnf";
const satSolver = require("./sat-solver"); import { naiveDpll } from "./sat-solver";
const isArray = Array.isArray; const isArray = Array.isArray;
@@ -26,7 +26,7 @@ function reduce(exp, callback) {
return exp; return exp;
} }
function evaluate(exp, obj, now, cb) { export function evaluate(exp, obj, now, cb) {
function getRegExp(pat, esc) { function getRegExp(pat, esc) {
const k = `${esc || ""}:${pat}`; const k = `${esc || ""}:${pat}`;
let c = regExpCache.get(exp); let c = regExpCache.get(exp);
@@ -35,7 +35,7 @@ function evaluate(exp, obj, now, cb) {
return c[k]; return c[k];
} }
return expressionParser.map(exp, e => { return map(exp, e => {
if (cb) e = cb(e); if (cb) e = cb(e);
if (!isArray(e)) return e; if (!isArray(e)) return e;
@@ -162,7 +162,7 @@ function evaluate(exp, obj, now, cb) {
}); });
} }
function and(exp1, exp2) { export function and(exp1, exp2) {
if (!isArray(exp1)) return exp1 ? exp2 : exp1; if (!isArray(exp1)) return exp1 ? exp2 : exp1;
if (!isArray(exp2)) return exp2 ? exp1 : exp2; if (!isArray(exp2)) return exp2 ? exp1 : exp2;
@@ -177,7 +177,7 @@ function and(exp1, exp2) {
return res; return res;
} }
function or(exp1, exp2) { export function or(exp1, exp2) {
if (!isArray(exp1)) return exp1 ? exp1 : exp2; if (!isArray(exp1)) return exp1 ? exp1 : exp2;
if (!isArray(exp2)) return exp2 ? exp2 : exp1; if (!isArray(exp2)) return exp2 ? exp2 : exp1;
@@ -192,19 +192,19 @@ function or(exp1, exp2) {
return res; return res;
} }
function not(exp) { export function not(exp) {
if (isArray(exp) && exp[0] === "NOT") return exp[1]; if (isArray(exp) && exp[0] === "NOT") return exp[1];
return ["NOT", exp]; return ["NOT", exp];
} }
function subset(exp1, exp2) { export function subset(exp1, exp2) {
const e = evaluate(["NOT", ["OR", ["NOT", exp1], exp2]]); const e = evaluate(["NOT", ["OR", ["NOT", exp1], exp2]]);
if (!isArray(e)) return !e; if (!isArray(e)) return !e;
const { vars, clauses } = expressionCnf.booleanCnf(e); const { vars, clauses } = booleanCnf(e);
return !satSolver.naiveDpll(clauses, vars); return !naiveDpll(clauses, vars);
} }
function likePatternToRegExp(pat, esc = "", flags = "") { export function likePatternToRegExp(pat, esc = "", flags = "") {
const convChars = { const convChars = {
"-": "\\-", "-": "\\-",
"/": "\\/", "/": "\\/",
@@ -225,7 +225,7 @@ function likePatternToRegExp(pat, esc = "", flags = "") {
"\\%": ".*", "\\%": ".*",
"\\_": "." "\\_": "."
}; };
let chars = expressionParser.parseLikePattern(pat, esc); let chars = parseLikePattern(pat, esc);
if (!chars.length) return new RegExp("^$", flags); if (!chars.length) return new RegExp("^$", flags);
chars = chars.map(c => convChars[c] || c); chars = chars.map(c => convChars[c] || c);
chars[0] = chars[0] === ".*" ? "" : "^" + chars[0]; chars[0] = chars[0] === ".*" ? "" : "^" + chars[0];
@@ -234,21 +234,11 @@ function likePatternToRegExp(pat, esc = "", flags = "") {
return new RegExp(chars.join(""), flags); return new RegExp(chars.join(""), flags);
} }
function extractParams(exp) { export function extractParams(exp) {
const params = new Set(); const params = new Set();
expressionParser.map(exp, e => { map(exp, e => {
if (isArray(e) && e[0] === "PARAM") params.add(e[1]); if (isArray(e) && e[0] === "PARAM") params.add(e[1]);
return e; return e;
}); });
return Array.from(params); return Array.from(params);
} }
exports.evaluate = evaluate;
exports.and = and;
exports.or = or;
exports.not = not;
exports.subset = subset;
exports.parse = expressionParser.parse;
exports.stringify = expressionParser.stringify;
exports.likePatternToRegExp = likePatternToRegExp;
exports.extractParams = extractParams;
+1 -3
View File
@@ -22,7 +22,7 @@ function getKey(obj) {
return k; return k;
} }
function memoize(func) { export default function memoize(func) {
return (...args) => { return (...args) => {
const key = JSON.stringify(args.map(getKey)) + getKey(func); const key = JSON.stringify(args.map(getKey)) + getKey(func);
@@ -43,5 +43,3 @@ const interval = setInterval(() => {
// Don't hold Node.js process // Don't hold Node.js process
if (interval.unref) interval.unref(); if (interval.unref) interval.unref();
module.exports = memoize;
+1 -3
View File
@@ -167,10 +167,8 @@ function unitPropagate(clauses, varVals) {
return derivedVars; return derivedVars;
} }
function naiveDpll(clauses, maxvarnr) { export function naiveDpll(clauses, maxvarnr) {
// variable values are 0 if not set, 1 if positive, -1 if negative // variable values are 0 if not set, 1 if positive, -1 if negative
const varVals = new Int32Array(maxvarnr + 1); const varVals = new Int32Array(maxvarnr + 1);
return satisfiableAt(clauses, varVals, 0, 0); return satisfiableAt(clauses, varVals, 0, 0);
} }
exports.naiveDpll = naiveDpll;
+1 -3
View File
@@ -1,5 +1,3 @@
function pass() { export default function pass() {
return 1; return 1;
} }
module.exports = pass;
+1 -3
View File
@@ -1,4 +1,4 @@
function test(resourceType, resource) { export default function test(resourceType, resource) {
if (resourceType === "devices") { if (resourceType === "devices") {
if (resource["Tags.test"] && resource["Tags.test"].value[0]) return 1; if (resource["Tags.test"] && resource["Tags.test"].value[0]) return 1;
} else if (resourceType === "presets") { } else if (resourceType === "presets") {
@@ -10,5 +10,3 @@ function test(resourceType, resource) {
} }
return -1; return -1;
} }
module.exports = test;
+9 -17
View File
@@ -18,8 +18,8 @@
*/ */
"use strict"; "use strict";
const path = require("path"); import { resolve } from "path";
const fs = require("fs"); import { readFileSync } from "fs";
const options = { const options = {
CONFIG_DIR: { type: "path", default: "config" }, CONFIG_DIR: { type: "path", default: "config" },
@@ -133,7 +133,7 @@ function setConfig(name, value, commandLineArgument) {
return String(val); return String(val);
case "path": case "path":
if (!val) return ""; if (!val) return "";
return path.resolve(val); return resolve(val);
default: default:
return null; return null;
} }
@@ -182,7 +182,7 @@ setConfig("CONFIG_DIR", options["CONFIG_DIR"]["default"]);
// Configuration file // Configuration file
const configFile = JSON.parse( const configFile = JSON.parse(
fs.readFileSync(path.resolve(allConfig.CONFIG_DIR, "config.json")) readFileSync(resolve(allConfig.CONFIG_DIR, "config.json"))
); );
for (const [k, v] of Object.entries(configFile)) { for (const [k, v] of Object.entries(configFile)) {
if (!setConfig(k, v)) if (!setConfig(k, v))
@@ -194,7 +194,7 @@ for (const [k, v] of Object.entries(configFile)) {
for (const [k, v] of Object.entries(options)) for (const [k, v] of Object.entries(options))
if (v["default"] != null) setConfig(k, v["default"]); if (v["default"] != null) setConfig(k, v["default"]);
function get(optionName, deviceId) { export function get(optionName, deviceId) {
if (!deviceId) return allConfig[optionName]; if (!deviceId) return allConfig[optionName];
optionName = `${optionName}-${deviceId}`; optionName = `${optionName}-${deviceId}`;
@@ -222,28 +222,20 @@ function get(optionName, deviceId) {
return null; return null;
} }
function getDefault(optionName) { export function getDefault(optionName) {
const option = options[optionName]; const option = options[optionName];
if (!option) return null; if (!option) return null;
let val = option["default"]; let val = option["default"];
if (val && option.type === "path") val = path.resolve(val); if (val && option.type === "path") val = resolve(val);
return val; return val;
} }
export let auth;
// Load authentication scripts // Load authentication scripts
try { try {
const authScript = fs.readFileSync( auth = require(resolve(allConfig.CONFIG_DIR, "auth.js"));
path.resolve(allConfig.CONFIG_DIR, "auth.js")
);
exports.auth = (function(exports) {
eval(authScript.toString());
return exports;
})({});
} catch (error) { } catch (error) {
// No auth.js exists // No auth.js exists
} }
exports.get = get;
exports.getDefault = getDefault;
+16 -19
View File
@@ -18,20 +18,20 @@
*/ */
"use strict"; "use strict";
const zlib = require("zlib"); import zlib from "zlib";
const crypto = require("crypto"); import crypto from "crypto";
const fs = require("fs"); import fs from "fs";
const config = require("./config"); import * as config from "./config";
const common = require("./common"); import * as common from "./common";
const soap = require("./soap"); import * as soap from "./soap";
const session = require("./session"); import * as session from "./session";
const query = require("./query"); import * as query from "./query";
const device = require("./device"); import * as device from "./device";
const cache = require("./cache"); import * as cache from "./cache";
const localCache = require("./local-cache"); import * as localCache from "./local-cache";
const db = require("./db"); import * as db from "./db";
const logger = require("./logger"); import * as logger from "./logger";
const scheduling = require("./scheduling"); import * as scheduling from "./scheduling";
const MAX_CYCLES = 4; const MAX_CYCLES = 4;
const MAX_CONCURRENT_REQUESTS = config.get("MAX_CONCURRENT_REQUESTS"); const MAX_CONCURRENT_REQUESTS = config.get("MAX_CONCURRENT_REQUESTS");
@@ -771,7 +771,7 @@ function getSession(connection, sessionId, callback) {
const currentSessions = new WeakMap(); const currentSessions = new WeakMap();
// When socket closes, store active sessions in cache // When socket closes, store active sessions in cache
function onConnection(socket) { export function onConnection(socket) {
// The property remoteAddress may be undefined after the connection is // The property remoteAddress may be undefined after the connection is
// closed, unless we read it at least once (caching?) // closed, unless we read it at least once (caching?)
socket.remoteAddress; socket.remoteAddress;
@@ -996,7 +996,7 @@ function processRequest(sessionContext, rpc) {
} }
} }
function listener(httpRequest, httpResponse) { export function listener(httpRequest, httpResponse) {
stats.totalRequests += 1; stats.totalRequests += 1;
if (httpRequest.method !== "POST") { if (httpRequest.method !== "POST") {
@@ -1243,6 +1243,3 @@ function listener(httpRequest, httpResponse) {
}); });
}); });
} }
exports.listener = listener;
exports.onConnection = onConnection;
+52 -69
View File
@@ -18,12 +18,12 @@
*/ */
"use strict"; "use strict";
const mongodb = require("mongodb"); import { MongoClient, ObjectID } from "mongodb";
const config = require("./config"); import { get } from "./config";
const common = require("./common"); import { escapeRegExp } from "./common";
const expressionParser = require("./common/expression-parser"); import { parse } from "./common/expression-parser";
let tasksCollection, export let tasksCollection,
devicesCollection, devicesCollection,
presetsCollection, presetsCollection,
objectsCollection, objectsCollection,
@@ -34,32 +34,28 @@ let tasksCollection,
operationsCollection, operationsCollection,
configCollection; configCollection;
function connect(callback) { export let client;
mongodb.MongoClient.connect(
config.get("MONGODB_CONNECTION_URL"), export function connect(callback) {
MongoClient.connect(
get("MONGODB_CONNECTION_URL"),
{ useNewUrlParser: true }, { useNewUrlParser: true },
(err, client) => { (err, _client) => {
if (err) return void callback(err); if (err) return void callback(err);
exports.client = client; client = _client;
const db = client.db(); const db = client.db();
exports.tasksCollection = tasksCollection = db.collection("tasks"); tasksCollection = db.collection("tasks");
tasksCollection.createIndex({ device: 1, timestamp: 1 }); tasksCollection.createIndex({ device: 1, timestamp: 1 });
exports.devicesCollection = devicesCollection = db.collection("devices"); devicesCollection = db.collection("devices");
exports.presetsCollection = presetsCollection = db.collection("presets"); presetsCollection = db.collection("presets");
exports.objectsCollection = objectsCollection = db.collection("objects"); objectsCollection = db.collection("objects");
exports.filesCollection = filesCollection = db.collection("fs.files"); filesCollection = db.collection("fs.files");
exports.provisionsCollection = provisionsCollection = db.collection( provisionsCollection = db.collection("provisions");
"provisions" virtualParametersCollection = db.collection("virtualParameters");
); faultsCollection = db.collection("faults");
exports.virtualParametersCollection = virtualParametersCollection = db.collection( operationsCollection = db.collection("operations");
"virtualParameters"
);
exports.faultsCollection = faultsCollection = db.collection("faults");
exports.operationsCollection = operationsCollection = db.collection(
"operations"
);
configCollection = db.collection("config"); configCollection = db.collection("config");
callback(); callback();
@@ -67,8 +63,8 @@ function connect(callback) {
); );
} }
function disconnect() { export function disconnect() {
if (exports.client) exports.client.close(); if (client) client.close();
} }
// Optimize projection by removing overlaps // Optimize projection by removing overlaps
@@ -92,8 +88,8 @@ function optimizeProjection(obj) {
return obj; return obj;
} }
function fetchDevice(id, timestamp, patterns, callback) { export function fetchDevice(id, timestamp, patterns, callback) {
const MAX_DEPTH = config.get("MAX_DEPTH", id); const MAX_DEPTH = get("MAX_DEPTH", id);
if (!patterns || !patterns.length) patterns = [[[], (1 << MAX_DEPTH) - 1]]; if (!patterns || !patterns.length) patterns = [[[], (1 << MAX_DEPTH) - 1]];
@@ -476,7 +472,13 @@ function fetchDevice(id, timestamp, patterns, callback) {
}); });
} }
function saveDevice(deviceId, deviceData, isNew, sessionTimestamp, callback) { export function saveDevice(
deviceId,
deviceData,
isNew,
sessionTimestamp,
callback
) {
const update = { $set: {}, $unset: {}, $addToSet: {}, $pull: {} }; const update = { $set: {}, $unset: {}, $addToSet: {}, $pull: {} };
for (const diff of deviceData.timestamps.diff()) { for (const diff of deviceData.timestamps.diff()) {
@@ -724,9 +726,9 @@ function saveDevice(deviceId, deviceData, isNew, sessionTimestamp, callback) {
); );
} }
function getFaults(deviceId, callback) { export function getFaults(deviceId, callback) {
faultsCollection faultsCollection
.find({ _id: { $regex: `^${common.escapeRegExp(deviceId)}\\:` } }) .find({ _id: { $regex: `^${escapeRegExp(deviceId)}\\:` } })
.toArray((err, res) => { .toArray((err, res) => {
if (err) return callback(err); if (err) return callback(err);
@@ -745,7 +747,7 @@ function getFaults(deviceId, callback) {
}); });
} }
function saveFault(deviceId, channel, fault, callback) { export function saveFault(deviceId, channel, fault, callback) {
const id = `${deviceId}:${channel}`; const id = `${deviceId}:${channel}`;
fault = Object.assign({}, fault); fault = Object.assign({}, fault);
fault._id = id; fault._id = id;
@@ -756,11 +758,11 @@ function saveFault(deviceId, channel, fault, callback) {
faultsCollection.replaceOne({ _id: id }, fault, { upsert: true }, callback); faultsCollection.replaceOne({ _id: id }, fault, { upsert: true }, callback);
} }
function deleteFault(deviceId, channel, callback) { export function deleteFault(deviceId, channel, callback) {
faultsCollection.deleteOne({ _id: `${deviceId}:${channel}` }, callback); faultsCollection.deleteOne({ _id: `${deviceId}:${channel}` }, callback);
} }
function getDueTasks(deviceId, timestamp, callback) { export function getDueTasks(deviceId, timestamp, callback) {
const cur = tasksCollection.find({ device: deviceId }).sort(["timestamp"]); const cur = tasksCollection.find({ device: deviceId }).sort(["timestamp"]);
const tasks = []; const tasks = [];
@@ -782,8 +784,8 @@ function getDueTasks(deviceId, timestamp, callback) {
// For API compatibility // For API compatibility
if (task.name === "download" && task.file) { if (task.name === "download" && task.file) {
let q; let q;
if (mongodb.ObjectID.isValid(task.file)) if (ObjectID.isValid(task.file))
q = { _id: { $in: [task.file, new mongodb.ObjectID(task.file)] } }; q = { _id: { $in: [task.file, new ObjectID(task.file)] } };
else q = { _id: task.file }; else q = { _id: task.file };
filesCollection.find(q).toArray((err, res) => { filesCollection.find(q).toArray((err, res) => {
@@ -805,16 +807,16 @@ function getDueTasks(deviceId, timestamp, callback) {
); );
} }
function clearTasks(deviceId, taskIds, callback) { export function clearTasks(deviceId, taskIds, callback) {
tasksCollection.deleteMany( tasksCollection.deleteMany(
{ _id: { $in: taskIds.map(id => new mongodb.ObjectID(id)) } }, { _id: { $in: taskIds.map(id => new ObjectID(id)) } },
callback callback
); );
} }
function getOperations(deviceId, callback) { export function getOperations(deviceId, callback) {
operationsCollection operationsCollection
.find({ _id: { $regex: `^${common.escapeRegExp(deviceId)}\\:` } }) .find({ _id: { $regex: `^${escapeRegExp(deviceId)}\\:` } })
.toArray((err, res) => { .toArray((err, res) => {
if (err) return void callback(err); if (err) return void callback(err);
@@ -833,7 +835,7 @@ function getOperations(deviceId, callback) {
}); });
} }
function saveOperation(deviceId, commandKey, operation, callback) { export function saveOperation(deviceId, commandKey, operation, callback) {
const id = `${deviceId}:${commandKey}`; const id = `${deviceId}:${commandKey}`;
operation = Object.assign({}, operation); operation = Object.assign({}, operation);
operation._id = id; operation._id = id;
@@ -849,34 +851,34 @@ function saveOperation(deviceId, commandKey, operation, callback) {
); );
} }
function deleteOperation(deviceId, commandKey, callback) { export function deleteOperation(deviceId, commandKey, callback) {
operationsCollection.deleteOne( operationsCollection.deleteOne(
{ _id: `${deviceId}:${commandKey}` }, { _id: `${deviceId}:${commandKey}` },
callback callback
); );
} }
function getPresets(callback) { export function getPresets(callback) {
presetsCollection.find().toArray(callback); presetsCollection.find().toArray(callback);
} }
function getObjects(callback) { export function getObjects(callback) {
objectsCollection.find().toArray(callback); objectsCollection.find().toArray(callback);
} }
function getProvisions(callback) { export function getProvisions(callback) {
provisionsCollection.find().toArray(callback); provisionsCollection.find().toArray(callback);
} }
function getVirtualParameters(callback) { export function getVirtualParameters(callback) {
virtualParametersCollection.find().toArray(callback); virtualParametersCollection.find().toArray(callback);
} }
function getFiles(callback) { export function getFiles(callback) {
filesCollection.find().toArray(callback); filesCollection.find().toArray(callback);
} }
function getConfig(callback) { export function getConfig(callback) {
configCollection.find().toArray((err, res) => { configCollection.find().toArray((err, res) => {
if (err) return void callback(err); if (err) return void callback(err);
@@ -884,27 +886,8 @@ function getConfig(callback) {
null, null,
res.map(c => ({ res.map(c => ({
id: c["_id"], id: c["_id"],
value: expressionParser.parse(c["value"]) value: parse(c["value"])
})) }))
); );
}); });
} }
exports.connect = connect;
exports.disconnect = disconnect;
exports.fetchDevice = fetchDevice;
exports.saveDevice = saveDevice;
exports.getFaults = getFaults;
exports.saveFault = saveFault;
exports.deleteFault = deleteFault;
exports.clearTasks = clearTasks;
exports.saveOperation = saveOperation;
exports.deleteOperation = deleteOperation;
exports.getPresets = getPresets;
exports.getObjects = getObjects;
exports.getProvisions = getProvisions;
exports.getVirtualParameters = getVirtualParameters;
exports.getFiles = getFiles;
exports.getDueTasks = getDueTasks;
exports.getOperations = getOperations;
exports.getConfig = getConfig;
+11 -19
View File
@@ -18,14 +18,14 @@
*/ */
"use strict"; "use strict";
const config = require("./config"); import * as config from "./config";
const common = require("./common"); import * as common from "./common";
const device = require("./device"); import * as device from "./device";
const scheduling = require("./scheduling"); import * as scheduling from "./scheduling";
const MAX_DEPTH = config.get("MAX_DEPTH"); const MAX_DEPTH = config.get("MAX_DEPTH");
function refresh(sessionContext, provision, declarations) { export function refresh(sessionContext, provision, declarations) {
const path = common.parsePath(provision[1]).slice(); const path = common.parsePath(provision[1]).slice();
const l = path.length; const l = path.length;
path.length = MAX_DEPTH; path.length = MAX_DEPTH;
@@ -48,7 +48,7 @@ function refresh(sessionContext, provision, declarations) {
return true; return true;
} }
function value(sessionContext, provision, declarations) { export function value(sessionContext, provision, declarations) {
const path = common.parsePath(provision[1]); const path = common.parsePath(provision[1]);
declarations.push({ declarations.push({
path: path, path: path,
@@ -62,7 +62,7 @@ function value(sessionContext, provision, declarations) {
return true; return true;
} }
function tag(sessionContext, provision, declarations) { export function tag(sessionContext, provision, declarations) {
const path = ["Tags", provision[1]]; const path = ["Tags", provision[1]];
declarations.push({ declarations.push({
@@ -77,7 +77,7 @@ function tag(sessionContext, provision, declarations) {
return true; return true;
} }
function reboot(sessionContext, provision, declarations) { export function reboot(sessionContext, provision, declarations) {
declarations.push({ declarations.push({
path: ["Reboot"], path: ["Reboot"],
pathGet: 1, pathGet: 1,
@@ -90,7 +90,7 @@ function reboot(sessionContext, provision, declarations) {
return true; return true;
} }
function reset(sessionContext, provision, declarations) { export function reset(sessionContext, provision, declarations) {
declarations.push({ declarations.push({
path: ["FactoryReset"], path: ["FactoryReset"],
pathGet: 1, pathGet: 1,
@@ -103,7 +103,7 @@ function reset(sessionContext, provision, declarations) {
return true; return true;
} }
function download(sessionContext, provision, declarations) { export function download(sessionContext, provision, declarations) {
const alias = [ const alias = [
["FileType"], ["FileType"],
provision[1] || "", provision[1] || "",
@@ -134,7 +134,7 @@ function download(sessionContext, provision, declarations) {
return true; return true;
} }
function instances( export function instances(
sessionContext, sessionContext,
provision, provision,
declarations, declarations,
@@ -178,11 +178,3 @@ function instances(
return true; return true;
} }
exports.refresh = refresh;
exports.value = value;
exports.tag = tag;
exports.reboot = reboot;
exports.reset = reset;
exports.download = download;
exports.instances = instances;
+15 -17
View File
@@ -32,7 +32,7 @@ function parseBool(v) {
else return null; else return null;
} }
function sanitizeParameterValue(parameterValue) { export function sanitizeParameterValue(parameterValue) {
if (parameterValue[0] != null) { if (parameterValue[0] != null) {
switch (parameterValue[1]) { switch (parameterValue[1]) {
case "xsd:boolean": case "xsd:boolean":
@@ -83,8 +83,8 @@ function sanitizeParameterValue(parameterValue) {
return parameterValue; return parameterValue;
} }
function getAliasDeclarations(path, timestamp) { export function getAliasDeclarations(path, timestamp) {
var decs = []; const decs = [];
function recursive(p, prefix) { function recursive(p, prefix) {
const pattern = prefix.concat(p); const pattern = prefix.concat(p);
@@ -113,7 +113,7 @@ function getAliasDeclarations(path, timestamp) {
return decs; return decs;
} }
function unpack(deviceData, path, revision) { export function unpack(deviceData, path, revision) {
let allMatches = []; let allMatches = [];
if (!path.alias) { if (!path.alias) {
for (const p of deviceData.paths.find(path, false, true)) for (const p of deviceData.paths.find(path, false, true))
@@ -194,7 +194,13 @@ function unpack(deviceData, path, revision) {
return allMatches; return allMatches;
} }
function clear(deviceData, path, timestamp, attributes, changeFlags = 0) { export function clear(
deviceData,
path,
timestamp,
attributes,
changeFlags = 0
) {
const changeTrackers = {}; const changeTrackers = {};
timestamp = timestamp || 0; timestamp = timestamp || 0;
@@ -254,7 +260,7 @@ function clear(deviceData, path, timestamp, attributes, changeFlags = 0) {
} }
function compareEquality(a, b) { function compareEquality(a, b) {
var t = typeof a; const t = typeof a;
if ( if (
a === null || a === null ||
a === undefined || a === undefined ||
@@ -268,7 +274,7 @@ function compareEquality(a, b) {
return JSON.stringify(a) === JSON.stringify(b); return JSON.stringify(a) === JSON.stringify(b);
} }
function set(deviceData, path, timestamp, attributes, toClear) { export function set(deviceData, path, timestamp, attributes, toClear) {
path = deviceData.paths.add(path); path = deviceData.paths.add(path);
const currentTimestamp = deviceData.timestamps.get(path); const currentTimestamp = deviceData.timestamps.get(path);
@@ -366,7 +372,7 @@ function set(deviceData, path, timestamp, attributes, toClear) {
return toClear; return toClear;
} }
function track(deviceData, path, marker, attributes) { export function track(deviceData, path, marker, attributes) {
path = deviceData.paths.add(path); path = deviceData.paths.add(path);
let f = 1; let f = 1;
@@ -382,7 +388,7 @@ function track(deviceData, path, marker, attributes) {
cur[marker] |= f; cur[marker] |= f;
} }
function clearTrackers(deviceData, tracker) { export function clearTrackers(deviceData, tracker) {
if (Array.isArray(tracker)) { if (Array.isArray(tracker)) {
for (const v of deviceData.trackers.values()) for (const v of deviceData.trackers.values())
for (const t of tracker) delete v[t]; for (const t of tracker) delete v[t];
@@ -392,11 +398,3 @@ function clearTrackers(deviceData, tracker) {
deviceData.changes.delete(tracker); deviceData.changes.delete(tracker);
} }
} }
exports.sanitizeParameterValue = sanitizeParameterValue;
exports.getAliasDeclarations = getAliasDeclarations;
exports.unpack = unpack;
exports.set = set;
exports.clear = clear;
exports.track = track;
exports.clearTrackers = clearTrackers;
+6 -10
View File
@@ -18,10 +18,9 @@
*/ */
"use strict"; "use strict";
const childProcess = require("child_process"); import { fork } from "child_process";
const crypto = require("crypto"); import crypto from "crypto";
import * as config from "./config";
const config = require("./config");
const TIMEOUT = config.get("EXT_TIMEOUT"); const TIMEOUT = config.get("EXT_TIMEOUT");
@@ -36,11 +35,11 @@ function messageHandler(message) {
} }
} }
function run(args, callback) { export function run(args, callback) {
const scriptName = args[0]; const scriptName = args[0];
if (!processes[scriptName]) { if (!processes[scriptName]) {
const p = childProcess.fork("lib/extension-wrapper", [scriptName]); const p = fork("lib/extension-wrapper", [scriptName]);
processes[scriptName] = p; processes[scriptName] = p;
p.on("error", () => { p.on("error", () => {
@@ -91,7 +90,7 @@ function kill(process, callback) {
}, 100); }, 100);
} }
function killAll(callback) { export function killAll(callback) {
let counter = 1; let counter = 1;
for (const n in processes) { for (const n in processes) {
++counter; ++counter;
@@ -102,6 +101,3 @@ function killAll(callback) {
} }
if (--counter === 0 && callback) callback(); if (--counter === 0 && callback) callback();
} }
exports.run = run;
exports.killAll = killAll;
+6 -8
View File
@@ -18,12 +18,12 @@
*/ */
"use strict"; "use strict";
const url = require("url"); import url from "url";
const querystring = require("querystring"); import querystring from "querystring";
const mongodb = require("mongodb"); import { GridFSBucket } from "mongodb";
const db = require("./db"); import * as db from "./db";
function listener(request, response) { export function listener(request, response) {
const urlParts = url.parse(request.url, true); const urlParts = url.parse(request.url, true);
if (request.method === "GET") { if (request.method === "GET") {
const filename = querystring.unescape(urlParts.pathname.substring(1)); const filename = querystring.unescape(urlParts.pathname.substring(1));
@@ -45,7 +45,7 @@ function listener(request, response) {
"Content-Length": file.length "Content-Length": file.length
}); });
const bucket = new mongodb.GridFSBucket(db.client.db()); const bucket = new GridFSBucket(db.client.db());
const downloadStream = bucket.openDownloadStream(filename); const downloadStream = bucket.openDownloadStream(filename);
downloadStream.pipe(response); downloadStream.pipe(response);
}); });
@@ -54,5 +54,3 @@ function listener(request, response) {
response.end("405 Method Not Allowed"); response.end("405 Method Not Allowed");
} }
} }
exports.listener = listener;
+1 -3
View File
@@ -28,7 +28,7 @@ const UNDISCOVERED_DEPTH = 7;
// flags is an int where its bits mark the segments in the pattern that // flags is an int where its bits mark the segments in the pattern that
// need refreshing. Leading 0s indicate that the pattern up to that // need refreshing. Leading 0s indicate that the pattern up to that
// point has been discovered. // point has been discovered.
function estimateGpnCount(gpnPatterns, depth = 0) { export function estimateGpnCount(gpnPatterns, depth = 0) {
const children = {}; const children = {};
const wildcardChildren = []; const wildcardChildren = [];
let wildcardDiscovered = false; let wildcardDiscovered = false;
@@ -77,5 +77,3 @@ function estimateGpnCount(gpnPatterns, depth = 0) {
return gpnCount; return gpnCount;
} }
exports.estimateGpnCount = estimateGpnCount;
+1 -3
View File
@@ -18,7 +18,7 @@
*/ */
"use strict"; "use strict";
class InstanceSet { export default class InstanceSet {
constructor() { constructor() {
this.set = new Set(); this.set = new Set();
} }
@@ -124,5 +124,3 @@ class InstanceSet {
return this.set.size; return this.set.size;
} }
} }
module.exports = InstanceSet;
+19 -30
View File
@@ -18,16 +18,16 @@
*/ */
"use strict"; "use strict";
const vm = require("vm"); import vm from "vm";
const crypto = require("crypto"); import crypto from "crypto";
const config = require("./config"); import * as config from "./config";
const db = require("./db"); import * as db from "./db";
const cache = require("./cache"); import * as cache from "./cache";
const query = require("./query"); import * as query from "./query";
const logger = require("./logger"); import * as logger from "./logger";
const scheduling = require("./scheduling"); import * as scheduling from "./scheduling";
const expression = require("./common/expression"); import * as expression from "./common/expression";
const REFRESH = 3000; const REFRESH = 3000;
const EVICT_TIMEOUT = 60000; const EVICT_TIMEOUT = 60000;
@@ -369,7 +369,7 @@ function refresh(callback) {
}); });
} }
function getCurrentSnapshot(callback) { export function getCurrentSnapshot(callback) {
if (Date.now() < nextRefresh) { if (Date.now() < nextRefresh) {
if (callback) return callback(null, currentSnapshot); if (callback) return callback(null, currentSnapshot);
return Promise.resolve(currentSnapshot); return Promise.resolve(currentSnapshot);
@@ -389,35 +389,35 @@ function getCurrentSnapshot(callback) {
}); });
} }
function hasSnapshot(hash) { export function hasSnapshot(hash) {
return snapshots.has(hash); return snapshots.has(hash);
} }
function getPresets(snapshotKey) { export function getPresets(snapshotKey) {
const snapshot = snapshots.get(snapshotKey); const snapshot = snapshots.get(snapshotKey);
if (!snapshot) throw new Error("Cache snapshot does not exist"); if (!snapshot) throw new Error("Cache snapshot does not exist");
return snapshot.presets; return snapshot.presets;
} }
function getProvisions(snapshotKey) { export function getProvisions(snapshotKey) {
const snapshot = snapshots.get(snapshotKey); const snapshot = snapshots.get(snapshotKey);
if (!snapshot) throw new Error("Cache snapshot does not exist"); if (!snapshot) throw new Error("Cache snapshot does not exist");
return snapshot.provisions; return snapshot.provisions;
} }
function getVirtualParameters(snapshotKey) { export function getVirtualParameters(snapshotKey) {
const snapshot = snapshots.get(snapshotKey); const snapshot = snapshots.get(snapshotKey);
if (!snapshot) throw new Error("Cache snapshot does not exist"); if (!snapshot) throw new Error("Cache snapshot does not exist");
return snapshot.virtualParameters; return snapshot.virtualParameters;
} }
function getFiles(snapshotKey) { export function getFiles(snapshotKey) {
const snapshot = snapshots.get(snapshotKey); const snapshot = snapshots.get(snapshotKey);
if (!snapshot) throw new Error("Cache snapshot does not exist"); if (!snapshot) throw new Error("Cache snapshot does not exist");
return snapshot.files; return snapshot.files;
} }
function getConfig(snapshotKey, key, context, now) { export function getConfig(snapshotKey, key, context, now) {
const snapshot = snapshots.get(snapshotKey); const snapshot = snapshots.get(snapshotKey);
if (!snapshot) throw new Error("Cache snapshot does not exist"); if (!snapshot) throw new Error("Cache snapshot does not exist");
@@ -445,34 +445,23 @@ function getConfig(snapshotKey, key, context, now) {
return expression.evaluate(snapshot.config[key], context, now || Date.now()); return expression.evaluate(snapshot.config[key], context, now || Date.now());
} }
function getAuthConfig(snapshotKey) { export function getAuthConfig(snapshotKey) {
const snapshot = snapshots.get(snapshotKey); const snapshot = snapshots.get(snapshotKey);
if (!snapshot) throw new Error("Cache snapshot does not exist"); if (!snapshot) throw new Error("Cache snapshot does not exist");
return snapshot.config.auth; return snapshot.config.auth;
} }
function getPermissionsConfig(snapshotKey) { export function getPermissionsConfig(snapshotKey) {
const snapshot = snapshots.get(snapshotKey); const snapshot = snapshots.get(snapshotKey);
if (!snapshot) throw new Error("Cache snapshot does not exist"); if (!snapshot) throw new Error("Cache snapshot does not exist");
return snapshot.config.permissions; return snapshot.config.permissions;
} }
function getUiConfig(snapshotKey) { export function getUiConfig(snapshotKey) {
const snapshot = snapshots.get(snapshotKey); const snapshot = snapshots.get(snapshotKey);
if (!snapshot) throw new Error("Cache snapshot does not exist"); if (!snapshot) throw new Error("Cache snapshot does not exist");
return snapshot.config.ui; return snapshot.config.ui;
} }
exports.getCurrentSnapshot = getCurrentSnapshot;
exports.hasSnapshot = hasSnapshot;
exports.getPresets = getPresets;
exports.getProvisions = getProvisions;
exports.getVirtualParameters = getVirtualParameters;
exports.getFiles = getFiles;
exports.getConfig = getConfig;
exports.getAuthConfig = getAuthConfig;
exports.getPermissionsConfig = getPermissionsConfig;
exports.getUiConfig = getUiConfig;
+13 -23
View File
@@ -18,10 +18,10 @@
*/ */
"use strict"; "use strict";
const fs = require("fs"); import fs from "fs";
const os = require("os"); import os from "os";
const config = require("./config"); import * as config from "./config";
const REOPEN_EVERY = 60000; const REOPEN_EVERY = 60000;
@@ -90,7 +90,7 @@ function reopen() {
setTimeout(reopen, REOPEN_EVERY - (Date.now() % REOPEN_EVERY)).unref(); setTimeout(reopen, REOPEN_EVERY - (Date.now() % REOPEN_EVERY)).unref();
} }
function init(service, version) { export function init(service, version) {
defaultMeta.hostname = os.hostname(); defaultMeta.hostname = os.hostname();
defaultMeta.pid = process.pid; defaultMeta.pid = process.pid;
defaultMeta.name = `genieacs-${service}`; defaultMeta.name = `genieacs-${service}`;
@@ -129,12 +129,12 @@ function init(service, version) {
setTimeout(reopen, REOPEN_EVERY - (Date.now() % REOPEN_EVERY)).unref(); setTimeout(reopen, REOPEN_EVERY - (Date.now() % REOPEN_EVERY)).unref();
} }
function close() { export function close() {
accessLogStream.end(); accessLogStream.end();
logStream.end(); logStream.end();
} }
function flatten(details) { export function flatten(details) {
if (details.sessionContext) { if (details.sessionContext) {
details.deviceId = details.sessionContext.deviceId; details.deviceId = details.sessionContext.deviceId;
details.remoteAddress = details.remoteAddress =
@@ -265,22 +265,22 @@ function log(details) {
} }
} }
function info(details) { export function info(details) {
details.severity = "info"; details.severity = "info";
log(details); log(details);
} }
function warn(details) { export function warn(details) {
details.severity = "warn"; details.severity = "warn";
log(details); log(details);
} }
function error(details) { export function error(details) {
details.severity = "error"; details.severity = "error";
log(details); log(details);
} }
function accessLog(details) { export function accessLog(details) {
details.timestamp = new Date().toISOString(); details.timestamp = new Date().toISOString();
if (ACCESS_LOG_FORMAT === "json") { if (ACCESS_LOG_FORMAT === "json") {
Object.assign(details, defaultMeta); Object.assign(details, defaultMeta);
@@ -290,27 +290,17 @@ function accessLog(details) {
} }
} }
function accessInfo(details) { export function accessInfo(details) {
details.severity = "info"; details.severity = "info";
accessLog(details); accessLog(details);
} }
function accessWarn(details) { export function accessWarn(details) {
details.severity = "warn"; details.severity = "warn";
accessLog(details); accessLog(details);
} }
function accessError(details) { export function accessError(details) {
details.severity = "error"; details.severity = "error";
accessLog(details); accessLog(details);
} }
exports.init = init;
exports.close = close;
exports.flatten = flatten;
exports.info = info;
exports.warn = warn;
exports.error = error;
exports.accessInfo = accessInfo;
exports.accessWarn = accessWarn;
exports.accessError = accessError;
+13 -16
View File
@@ -18,18 +18,17 @@
*/ */
"use strict"; "use strict";
const url = require("url"); import url from "url";
const mongodb = require("mongodb"); import mongodb from "mongodb";
const querystring = require("querystring"); import querystring from "querystring";
const vm = require("vm"); import vm from "vm";
const childProcess = require("child_process"); import { exec } from "child_process";
const config = require("./config"); import * as config from "./config";
const db = require("./db"); import * as db from "./db";
const query = require("./query"); import * as query from "./query";
const apiFunctions = require("./api-functions"); import * as apiFunctions from "./api-functions";
const cache = require("./cache"); import * as cache from "./cache";
import { version as VERSION } from "../package.json";
const VERSION = require("../package.json").version;
const DEVICE_TASKS_REGEX = /^\/devices\/([a-zA-Z0-9\-_%]+)\/tasks\/?$/; const DEVICE_TASKS_REGEX = /^\/devices\/([a-zA-Z0-9\-_%]+)\/tasks\/?$/;
const TASKS_REGEX = /^\/tasks\/([a-zA-Z0-9\-_%]+)(\/[a-zA-Z_]*)?$/; const TASKS_REGEX = /^\/tasks\/([a-zA-Z0-9\-_%]+)(\/[a-zA-Z_]*)?$/;
@@ -52,7 +51,7 @@ function throwError(err, httpResponse) {
throw err; throw err;
} }
function listener(request, response) { export function listener(request, response) {
const chunks = []; const chunks = [];
let bytes = 0; let bytes = 0;
response.setHeader("GenieACS-Version", VERSION); response.setHeader("GenieACS-Version", VERSION);
@@ -520,7 +519,7 @@ function listener(request, response) {
} }
} else if (PING_REGEX.test(urlParts.pathname)) { } else if (PING_REGEX.test(urlParts.pathname)) {
const host = querystring.unescape(PING_REGEX.exec(urlParts.pathname)[1]); const host = querystring.unescape(PING_REGEX.exec(urlParts.pathname)[1]);
childProcess.exec(`ping -w 1 -i 0.2 -c 3 ${host}`, (err, stdout) => { exec(`ping -w 1 -i 0.2 -c 3 ${host}`, (err, stdout) => {
if (err) { if (err) {
response.writeHead(404, { "Cache-Control": "no-cache" }); response.writeHead(404, { "Cache-Control": "no-cache" });
response.end(`${err.name}: ${err.message}`); response.end(`${err.name}: ${err.message}`);
@@ -661,5 +660,3 @@ function listener(request, response) {
} }
}); });
} }
exports.listener = listener;
+1 -3
View File
@@ -18,7 +18,7 @@
*/ */
"use strict"; "use strict";
class PathSet { export default class PathSet {
constructor() { constructor() {
this.lengthIndex = []; this.lengthIndex = [];
this.fragmentIndex = []; this.fragmentIndex = [];
@@ -161,5 +161,3 @@ class PathSet {
return res; return res;
} }
} }
module.exports = PathSet;
+7 -13
View File
@@ -18,7 +18,7 @@
*/ */
"use strict"; "use strict";
const common = require("./common"); import * as common from "./common";
function stringToRegexp(input, flags) { function stringToRegexp(input, flags) {
if (input.indexOf("*") === -1) return false; if (input.indexOf("*") === -1) return false;
@@ -53,7 +53,7 @@ function normalize(input) {
return input; return input;
} }
function expandValue(value) { export function expandValue(value) {
if (common.typeOf(value) === common.ARRAY_TYPE) { if (common.typeOf(value) === common.ARRAY_TYPE) {
let a = []; let a = [];
for (const j of value) a = a.conact(expandValue(j)); for (const j of value) a = a.conact(expandValue(j));
@@ -104,7 +104,7 @@ function permute(param, val) {
return conditions; return conditions;
} }
function expand(query) { export function expand(query) {
const newQuery = {}; const newQuery = {};
for (const [k, v] of Object.entries(query)) { for (const [k, v] of Object.entries(query)) {
if (k[0] === "$") { if (k[0] === "$") {
@@ -157,7 +157,7 @@ function testExpressions(params, expressions, lop) {
} }
} }
function test(params, query) { export function test(params, query) {
let res; let res;
for (const [k, v] of Object.entries(query)) { for (const [k, v] of Object.entries(query)) {
if (k.charAt(0) === "$") { if (k.charAt(0) === "$") {
@@ -233,7 +233,7 @@ function matchType(src, dst) {
return dst; return dst;
} }
function testFilter(obj, filter) { export function testFilter(obj, filter) {
for (const [k, v] of Object.entries(filter)) { for (const [k, v] of Object.entries(filter)) {
const [param, op] = k.split(/([^a-zA-Z0-9\-_.].*)/, 2); const [param, op] = k.split(/([^a-zA-Z0-9\-_.].*)/, 2);
const val = matchType(obj[param], v); const val = matchType(obj[param], v);
@@ -264,7 +264,7 @@ function testFilter(obj, filter) {
return true; return true;
} }
function convertMongoQueryToFilters(query, filters) { export function convertMongoQueryToFilters(query, filters) {
filters = filters || {}; filters = filters || {};
for (let [k, v] of Object.entries(query)) { for (let [k, v] of Object.entries(query)) {
if (k[0] === "$") { if (k[0] === "$") {
@@ -346,7 +346,7 @@ function convertMongoQueryToFilters(query, filters) {
return filters; return filters;
} }
function sanitizeQueryTypes(query, types) { export function sanitizeQueryTypes(query, types) {
for (const [k, v] of Object.entries(query)) { for (const [k, v] of Object.entries(query)) {
if (k[0] === "$") { if (k[0] === "$") {
// Logical operator // Logical operator
@@ -383,9 +383,3 @@ function sanitizeQueryTypes(query, types) {
return query; return query;
} }
exports.expand = expand;
exports.test = test;
exports.sanitizeQueryTypes = sanitizeQueryTypes;
exports.convertMongoQueryToFilters = convertMongoQueryToFilters;
exports.testFilter = testFilter;
+8 -10
View File
@@ -18,15 +18,15 @@
*/ */
"use strict"; "use strict";
const vm = require("vm"); import vm from "vm";
const seedrandom = require("seedrandom"); import seedrandom from "seedrandom";
const common = require("./common"); import * as common from "./common";
const device = require("./device"); import * as device from "./device";
const extensions = require("./extensions"); import * as extensions from "./extensions";
const logger = require("./logger"); import * as logger from "./logger";
const scheduling = require("./scheduling"); import * as scheduling from "./scheduling";
// Used for throwing to exit user script and commit // Used for throwing to exit user script and commit
const COMMIT = Symbol(); const COMMIT = Symbol();
@@ -327,7 +327,7 @@ function errorToFault(err) {
return fault; return fault;
} }
function run( export function run(
script, script,
globals, globals,
sessionContext, sessionContext,
@@ -416,5 +416,3 @@ function run(
} }
} }
} }
exports.run = run;
+6 -11
View File
@@ -18,9 +18,9 @@
*/ */
"use strict"; "use strict";
const crypto = require("crypto"); import crypto from "crypto";
const later = require("later"); import later from "later";
function md532(str) { function md532(str) {
const digest = crypto const digest = crypto
@@ -35,22 +35,22 @@ function md532(str) {
); );
} }
function variance(deviceId, vrnc) { export function variance(deviceId, vrnc) {
return (md532(deviceId) >>> 0) % vrnc; return (md532(deviceId) >>> 0) % vrnc;
} }
function interval(timestamp, intrvl, offset = 0) { export function interval(timestamp, intrvl, offset = 0) {
return Math.trunc((timestamp + offset) / intrvl) * intrvl - offset; return Math.trunc((timestamp + offset) / intrvl) * intrvl - offset;
} }
function parseCron(cronExp) { export function parseCron(cronExp) {
const parts = cronExp.trim().split(/\s+/); const parts = cronExp.trim().split(/\s+/);
if (parts.length === 5) parts.unshift("*"); if (parts.length === 5) parts.unshift("*");
return later.schedule(later.parse.cron(parts.join(" "), true)); return later.schedule(later.parse.cron(parts.join(" "), true));
} }
function cron(timestamp, schedule, offset = 0) { export function cron(timestamp, schedule, offset = 0) {
// TODO later.js doesn't throw erorr if expression is invalid! // TODO later.js doesn't throw erorr if expression is invalid!
const ret = [0, 0]; const ret = [0, 0];
@@ -62,8 +62,3 @@ function cron(timestamp, schedule, offset = 0) {
return ret; return ret;
} }
exports.variance = variance;
exports.interval = interval;
exports.parseCron = parseCron;
exports.cron = cron;
+5 -8
View File
@@ -18,9 +18,9 @@
*/ */
"use strict"; "use strict";
const fs = require("fs"); import fs from "fs";
const http = require("http"); import http from "http";
const https = require("https"); import https from "https";
let server, listener; let server, listener;
@@ -48,7 +48,7 @@ function closeServer(timeout, callback) {
}); });
} }
function start(port, networkInterface, ssl, _listener, onConnection) { export function start(port, networkInterface, ssl, _listener, onConnection) {
listener = _listener; listener = _listener;
if (ssl) { if (ssl) {
@@ -76,7 +76,7 @@ function start(port, networkInterface, ssl, _listener, onConnection) {
server.listen(port, networkInterface); server.listen(port, networkInterface);
} }
function stop() { export function stop() {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
setTimeout(() => { setTimeout(() => {
reject(new Error("Could not close server in a timely manner")); reject(new Error("Could not close server in a timely manner"));
@@ -84,6 +84,3 @@ function stop() {
closeServer(20000, resolve); closeServer(20000, resolve);
}); });
} }
exports.start = start;
exports.stop = stop;
+23 -35
View File
@@ -18,17 +18,17 @@
*/ */
"use strict"; "use strict";
const config = require("./config"); import * as config from "./config";
const common = require("./common"); import * as common from "./common";
const db = require("./db"); import * as db from "./db";
const device = require("./device"); import * as device from "./device";
const sandbox = require("./sandbox"); import * as sandbox from "./sandbox";
const localCache = require("./local-cache"); import * as localCache from "./local-cache";
const PathSet = require("./path-set"); import * as defaultProvisions from "./default-provisions";
const VersionedMap = require("./versioned-map"); import { estimateGpnCount } from "./gpn-heuristic";
const InstanceSet = require("./instance-set"); import PathSet from "./path-set";
const defaultProvisions = require("./default-provisions"); import VersionedMap from "./versioned-map";
const gpnHeuristic = require("./gpn-heuristic"); import InstanceSet from "./instance-set";
function initDeviceData() { function initDeviceData() {
return { return {
@@ -41,7 +41,7 @@ function initDeviceData() {
}; };
} }
function init(deviceId, cwmpVersion, timeout, callback) { export function init(deviceId, cwmpVersion, timeout, callback) {
const timestamp = Date.now(); const timestamp = Date.now();
const sessionContext = { const sessionContext = {
timestamp: timestamp, timestamp: timestamp,
@@ -112,7 +112,7 @@ function generateRpcId(sessionContext) {
); );
} }
function inform(sessionContext, rpcReq, callback) { export function inform(sessionContext, rpcReq, callback) {
const timestamp = sessionContext.timestamp + sessionContext.iteration + 1; const timestamp = sessionContext.timestamp + sessionContext.iteration + 1;
const params = [ const params = [
[ [
@@ -244,7 +244,7 @@ function inform(sessionContext, rpcReq, callback) {
}); });
} }
function transferComplete(sessionContext, rpcReq, callback) { export function transferComplete(sessionContext, rpcReq, callback) {
const revision = const revision =
(sessionContext.revisions[sessionContext.revisions.length - 1] || 0) + 1; (sessionContext.revisions[sessionContext.revisions.length - 1] || 0) + 1;
sessionContext.deviceData.timestamps.revision = revision; sessionContext.deviceData.timestamps.revision = revision;
@@ -410,7 +410,7 @@ function revertDownloadParameters(sessionContext, instance, callback) {
}); });
} }
function timeoutOperations(sessionContext, callback) { export function timeoutOperations(sessionContext, callback) {
const revision = const revision =
(sessionContext.revisions[sessionContext.revisions.length - 1] || 0) + 1; (sessionContext.revisions[sessionContext.revisions.length - 1] || 0) + 1;
sessionContext.deviceData.timestamps.revision = revision; sessionContext.deviceData.timestamps.revision = revision;
@@ -460,7 +460,7 @@ function timeoutOperations(sessionContext, callback) {
if ((counter -= 2) === 1) callback(null, faults, operations); if ((counter -= 2) === 1) callback(null, faults, operations);
} }
function addProvisions(sessionContext, channel, provisions) { export function addProvisions(sessionContext, channel, provisions) {
// Multiply by two because every iteration is two // Multiply by two because every iteration is two
// phases: read and update // phases: read and update
const MAX_ITERATIONS = const MAX_ITERATIONS =
@@ -513,7 +513,7 @@ function addProvisions(sessionContext, channel, provisions) {
} }
} }
function clearProvisions(sessionContext) { export function clearProvisions(sessionContext) {
// Multiply by two because every iteration is two // Multiply by two because every iteration is two
// phases: read and update // phases: read and update
const MAX_ITERATIONS = const MAX_ITERATIONS =
@@ -958,7 +958,7 @@ function runDeclarations(sessionContext, declarations) {
); );
} }
function rpcRequest(sessionContext, _declarations, callback) { export function rpcRequest(sessionContext, _declarations, callback) {
if (sessionContext.rpcRequest != null) { if (sessionContext.rpcRequest != null) {
return void callback( return void callback(
null, null,
@@ -1560,7 +1560,7 @@ function generateGetRpcRequest(sessionContext) {
const v = syncState.gpnPatterns.get(p); const v = syncState.gpnPatterns.get(p);
if (v) patterns.push([p, (v >> path.length) << path.length]); if (v) patterns.push([p, (v >> path.length) << path.length]);
} }
est = gpnHeuristic.estimateGpnCount(patterns); est = estimateGpnCount(patterns);
} }
if (est < Math.pow(2, Math.max(0, 8 - path.length))) { if (est < Math.pow(2, Math.max(0, 8 - path.length))) {
@@ -2250,7 +2250,7 @@ function clear(sessionContext, toClear, callback) {
}); });
} }
function rpcResponse(sessionContext, id, rpcRes, callback) { export function rpcResponse(sessionContext, id, rpcRes, callback) {
if (id !== generateRpcId(sessionContext)) if (id !== generateRpcId(sessionContext))
return void callback(new Error("Request ID not recognized")); return void callback(new Error("Request ID not recognized"));
@@ -2583,7 +2583,7 @@ function rpcResponse(sessionContext, id, rpcRes, callback) {
clear(sessionContext, toClear, callback); clear(sessionContext, toClear, callback);
} }
function rpcFault(sessionContext, id, faultResponse, callback) { export function rpcFault(sessionContext, id, faultResponse, callback) {
const rpcReq = sessionContext.rpcRequest; const rpcReq = sessionContext.rpcRequest;
delete sessionContext.syncState; delete sessionContext.syncState;
delete sessionContext.rpcRequest; delete sessionContext.rpcRequest;
@@ -2627,7 +2627,7 @@ function rpcFault(sessionContext, id, faultResponse, callback) {
callback(null, fault); callback(null, fault);
} }
function deserialize(sessionContextString, callback) { export function deserialize(sessionContextString, callback) {
const sessionContext = JSON.parse(sessionContextString); const sessionContext = JSON.parse(sessionContextString);
for (const decs of sessionContext.declarations) for (const decs of sessionContext.declarations)
@@ -2654,7 +2654,7 @@ function deserialize(sessionContextString, callback) {
}); });
} }
function serialize(sessionContext, callback) { export function serialize(sessionContext, callback) {
const deviceData = []; const deviceData = [];
for (const path of sessionContext.deviceData.paths.find( for (const path of sessionContext.deviceData.paths.find(
@@ -2682,15 +2682,3 @@ function serialize(sessionContext, callback) {
callback(null, sessionContextString); callback(null, sessionContextString);
} }
exports.init = init;
exports.timeoutOperations = timeoutOperations;
exports.inform = inform;
exports.transferComplete = transferComplete;
exports.addProvisions = addProvisions;
exports.clearProvisions = clearProvisions;
exports.rpcRequest = rpcRequest;
exports.rpcResponse = rpcResponse;
exports.rpcFault = rpcFault;
exports.serialize = serialize;
exports.deserialize = deserialize;
+6 -9
View File
@@ -18,11 +18,11 @@
*/ */
"use strict"; "use strict";
const libxmljs = require("libxmljs"); import libxmljs from "libxmljs";
import * as config from "./config";
import { version as VERSION } from "../package.json";
const config = require("./config"); const SERVER_NAME = `GenieACS/${VERSION}`;
const SERVER_NAME = `GenieACS/${require("../package.json").version}`;
const NAMESPACES = { const NAMESPACES = {
"1.0": { "1.0": {
@@ -518,7 +518,7 @@ function fault(xml) {
return { faultCode, faultString, detail }; return { faultCode, faultString, detail };
} }
function request(data, cwmpVersion, warn) { export function request(data, cwmpVersion, warn) {
warnings = warn; warnings = warn;
const rpc = { const rpc = {
@@ -642,7 +642,7 @@ function createSoapEnv(cwmpVersion) {
return env; return env;
} }
function response(rpc) { export function response(rpc) {
const headers = { const headers = {
Server: SERVER_NAME, Server: SERVER_NAME,
SOAPServer: SERVER_NAME SOAPServer: SERVER_NAME
@@ -716,6 +716,3 @@ function response(rpc) {
data: env.doc().toString(LIBXMLJS_SAVE_OPTIONS) data: env.doc().toString(LIBXMLJS_SAVE_OPTIONS)
}; };
} }
exports.request = request;
exports.response = response;
+17 -17
View File
@@ -1,19 +1,19 @@
"use strict"; "use strict";
const Koa = require("koa"); import { Z_SYNC_FLUSH } from "zlib";
const Router = require("koa-router"); import Koa from "koa";
const koaStatic = require("koa-static"); import Router from "koa-router";
const koaCompress = require("koa-compress"); import * as jwt from "jsonwebtoken";
const koaBodyParser = require("koa-bodyparser"); import koaStatic from "koa-static";
const koaJwt = require("koa-jwt"); import koaCompress from "koa-compress";
const jwt = require("jsonwebtoken"); import koaBodyParser from "koa-bodyparser";
import koaJwt from "koa-jwt";
const config = require("./config"); import * as config from "./config";
const api = require("./ui/api"); import api from "./ui/api";
const Authorizer = require("./common/authorizer"); import Authorizer from "./common/authorizer";
const expression = require("./common/expression"); import { parse } from "./common/expression-parser";
const logger = require("./logger"); import * as logger from "./logger";
const localCache = require("./local-cache"); import * as localCache from "./local-cache";
const koa = new Koa(); const koa = new Koa();
const router = new Router(); const router = new Router();
@@ -41,7 +41,7 @@ function getPermissionSets(ctx) {
].includes(k) ].includes(k)
) { ) {
if (v.filter == null || v.filter === "") v.filter = true; if (v.filter == null || v.filter === "") v.filter = true;
else v.filter = expression.parse(v.filter); else v.filter = parse(v.filter);
} }
} }
return p; return p;
@@ -175,11 +175,11 @@ router.get("/", async ctx => {
koa.use( koa.use(
koaCompress({ koaCompress({
flush: require("zlib").Z_SYNC_FLUSH flush: Z_SYNC_FLUSH
}) })
); );
koa.use(router.routes()); koa.use(router.routes());
koa.use(koaStatic("./public")); koa.use(koaStatic("./public"));
exports.listener = koa.callback(); export const listener = koa.callback();
+13 -22
View File
@@ -1,14 +1,13 @@
"use strict"; "use strict";
const http = require("http"); import http from "http";
const https = require("https"); import https from "https";
const url = require("url"); import url from "url";
import * as config from "../config";
import * as db from "./db";
import * as mongodbFunctions from "./mongodb-functions";
const config = require("../config"); export function filterToMongoQuery(filter, negate = false, res = {}) {
const db = require("./db");
const mongodbFunctions = require("./mongodb-functions");
function filterToMongoQuery(filter, negate = false, res = {}) {
const op = filter[0]; const op = filter[0];
if ((!negate && op === "AND") || (negate && op === "OR")) { if ((!negate && op === "AND") || (negate && op === "OR")) {
@@ -66,7 +65,7 @@ function filterToMongoQuery(filter, negate = false, res = {}) {
return res; return res;
} }
function deleteResource(resource, id) { export function deleteResource(resource, id) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const resourceUrl = const resourceUrl =
resource === "virtualParameters" ? "virtual_parameters" : resource; resource === "virtualParameters" ? "virtual_parameters" : resource;
@@ -142,7 +141,7 @@ function postTask(task, connectionRequest, callback) {
.end(JSON.stringify(task)); .end(JSON.stringify(task));
} }
function postTasks(deviceId, tasks) { export function postTasks(deviceId, tasks) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let connectionRequestStatus; let connectionRequestStatus;
const promises = []; const promises = [];
@@ -206,7 +205,7 @@ function postTasks(deviceId, tasks) {
}); });
} }
function updateTags(deviceId, tags) { export function updateTags(deviceId, tags) {
return Promise.all( return Promise.all(
Object.entries(tags).map( Object.entries(tags).map(
([tag, onOff]) => ([tag, onOff]) =>
@@ -235,7 +234,7 @@ function updateTags(deviceId, tags) {
); );
} }
function ping(host) { export function ping(host) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const options = url.parse( const options = url.parse(
`http://127.0.0.1:${config.get("NBI_PORT")}/ping/${encodeURIComponent( `http://127.0.0.1:${config.get("NBI_PORT")}/ping/${encodeURIComponent(
@@ -295,7 +294,7 @@ function ping(host) {
}); });
} }
function putResource(resource, id, data) { export function putResource(resource, id, data) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const resourceUrl = const resourceUrl =
resource === "virtualParameters" ? "virtual_parameters" : resource; resource === "virtualParameters" ? "virtual_parameters" : resource;
@@ -348,7 +347,7 @@ function putResource(resource, id, data) {
}); });
} }
function putFile(id, metadata, fileStream) { export function putFile(id, metadata, fileStream) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const options = url.parse( const options = url.parse(
`http://127.0.0.1:${config.get("NBI_PORT")}/files/${encodeURIComponent( `http://127.0.0.1:${config.get("NBI_PORT")}/files/${encodeURIComponent(
@@ -372,11 +371,3 @@ function putFile(id, metadata, fileStream) {
fileStream.pipe(req); fileStream.pipe(req);
}); });
} }
exports.postTasks = postTasks;
exports.deleteResource = deleteResource;
exports.putResource = putResource;
exports.updateTags = updateTags;
exports.filterToMongoQuery = filterToMongoQuery;
exports.ping = ping;
exports.putFile = putFile;
+17 -20
View File
@@ -1,13 +1,15 @@
"use strict"; "use strict";
const stream = require("stream"); import stream from "stream";
const Router = require("koa-router"); import Router from "koa-router";
const db = require("./db"); import * as db from "./db";
const apiFunctions = require("./api-functions"); import * as apiFunctions from "./api-functions";
const expression = require("../common/expression"); import { evaluate, and, extractParams } from "../common/expression";
const logger = require("../logger"); import { parse } from "../common/expression-parser";
import * as logger from "../logger";
const router = new Router(); const router = new Router();
export default router;
function logUnauthorizedWarning(log) { function logUnauthorizedWarning(log) {
log.message += " not authorized"; log.message += " not authorized";
@@ -87,8 +89,7 @@ router.get(`/devices/:id.csv`, async ctx => {
for (const [resource, flags] of Object.entries(resources)) { for (const [resource, flags] of Object.entries(resources)) {
router.head(`/${resource}`, async (ctx, next) => { router.head(`/${resource}`, async (ctx, next) => {
let filter = true; let filter = true;
if (ctx.request.query.filter) if (ctx.request.query.filter) filter = parse(ctx.request.query.filter);
filter = expression.parse(ctx.request.query.filter);
const log = { const log = {
message: `Count ${resource}`, message: `Count ${resource}`,
@@ -103,7 +104,7 @@ for (const [resource, flags] of Object.entries(resources)) {
// Exclude temporary tasks and faults // Exclude temporary tasks and faults
if (resource === "tasks" || resource === "faults") { if (resource === "tasks" || resource === "faults") {
filter = expression.and(filter, [ filter = and(filter, [
"NOT", "NOT",
["<", ["PARAM", "expiry"], Date.now() + 60000] ["<", ["PARAM", "expiry"], Date.now() + 60000]
]); ]);
@@ -121,8 +122,7 @@ for (const [resource, flags] of Object.entries(resources)) {
router.get(`/${resource}`, async (ctx, next) => { router.get(`/${resource}`, async (ctx, next) => {
const options = {}; const options = {};
let filter = true; let filter = true;
if (ctx.request.query.filter) if (ctx.request.query.filter) filter = parse(ctx.request.query.filter);
filter = expression.parse(ctx.request.query.filter);
if (ctx.request.query.limit) options.limit = +ctx.request.query.limit; if (ctx.request.query.limit) options.limit = +ctx.request.query.limit;
if (ctx.request.query.skip) options.skip = +ctx.request.query.skip; if (ctx.request.query.skip) options.skip = +ctx.request.query.skip;
if (ctx.request.query.sort) if (ctx.request.query.sort)
@@ -150,7 +150,7 @@ for (const [resource, flags] of Object.entries(resources)) {
// Exclude temporary tasks and faults // Exclude temporary tasks and faults
if (resource === "tasks" || resource === "faults") { if (resource === "tasks" || resource === "faults") {
filter = expression.and(filter, [ filter = and(filter, [
"NOT", "NOT",
["<", ["PARAM", "expiry"], Date.now() + 60000] ["<", ["PARAM", "expiry"], Date.now() + 60000]
]); ]);
@@ -173,8 +173,7 @@ for (const [resource, flags] of Object.entries(resources)) {
router.get(`/${resource}.csv`, async (ctx, next) => { router.get(`/${resource}.csv`, async (ctx, next) => {
const options = { projection: {} }; const options = { projection: {} };
let filter = true; let filter = true;
if (ctx.request.query.filter) if (ctx.request.query.filter) filter = parse(ctx.request.query.filter);
filter = expression.parse(ctx.request.query.filter);
if (ctx.request.query.limit) options.limit = +ctx.request.query.limit; if (ctx.request.query.limit) options.limit = +ctx.request.query.limit;
if (ctx.request.query.skip) options.skip = +ctx.request.query.skip; if (ctx.request.query.skip) options.skip = +ctx.request.query.skip;
if (ctx.request.query.sort) if (ctx.request.query.sort)
@@ -198,14 +197,14 @@ for (const [resource, flags] of Object.entries(resources)) {
const now = Date.now(); const now = Date.now();
for (const [k, v] of Object.entries(columns)) { for (const [k, v] of Object.entries(columns)) {
const e = expression.evaluate(expression.parse(v), null, now); const e = evaluate(parse(v), null, now);
columns[k] = e; columns[k] = e;
for (const p of expression.extractParams(e)) options.projection[p] = 1; for (const p of extractParams(e)) options.projection[p] = 1;
} }
// Exclude temporary tasks and faults // Exclude temporary tasks and faults
if (resource === "tasks" || resource === "faults") { if (resource === "tasks" || resource === "faults") {
filter = expression.and(filter, [ filter = and(filter, [
"NOT", "NOT",
["<", ["PARAM", "expiry"], Date.now() + 60000] ["<", ["PARAM", "expiry"], Date.now() + 60000]
]); ]);
@@ -222,7 +221,7 @@ for (const [resource, flags] of Object.entries(resources)) {
); );
await db.query(resource, filter, options, obj => { await db.query(resource, filter, options, obj => {
const arr = Object.values(columns).map(exp => { const arr = Object.values(columns).map(exp => {
const v = expression.evaluate(exp, obj, null, e => { const v = evaluate(exp, obj, null, e => {
if (Array.isArray(e)) { if (Array.isArray(e)) {
if (e[0] === "PARAM") { if (e[0] === "PARAM") {
if (resource === "devices") { if (resource === "devices") {
@@ -481,5 +480,3 @@ router.post("/devices/:id/tags", async (ctx, next) => {
router.get("/ping/:host", async ctx => { router.get("/ping/:host", async ctx => {
ctx.body = await apiFunctions.ping(ctx.params.host); ctx.body = await apiFunctions.ping(ctx.params.host);
}); });
module.exports = router;
+9 -14
View File
@@ -1,9 +1,9 @@
"use strict"; "use strict";
const mongodb = require("mongodb"); import { MongoClient } from "mongodb";
const config = require("../config"); import * as config from "../config";
const mongodbFunctions = require("./mongodb-functions"); import * as mongodbFunctions from "./mongodb-functions";
const expression = require("../common/expression"); import * as expression from "../common/expression";
const CACHE_TTL = 300000; const CACHE_TTL = 300000;
@@ -24,7 +24,7 @@ function getClient() {
if (!_clientPromise) { if (!_clientPromise) {
_clientPromise = new Promise((resolve, reject) => { _clientPromise = new Promise((resolve, reject) => {
const CONNECTION_URL = config.get("MONGODB_CONNECTION_URL"); const CONNECTION_URL = config.get("MONGODB_CONNECTION_URL");
mongodb.MongoClient.connect( MongoClient.connect(
CONNECTION_URL, CONNECTION_URL,
{ useNewUrlParser: true }, { useNewUrlParser: true },
(err, client) => { (err, client) => {
@@ -39,7 +39,7 @@ function getClient() {
return _clientPromise; return _clientPromise;
} }
function cache(key, valueGetter, ttl) { export function cache(key, valueGetter, ttl) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
getClient() getClient()
.then(client => { .then(client => {
@@ -72,7 +72,7 @@ function cache(key, valueGetter, ttl) {
}); });
} }
function query(resource, filter, options, callback) { export function query(resource, filter, options, callback) {
options = options || {}; options = options || {};
let q; let q;
filter = expression.evaluate(filter, null, Date.now()); filter = expression.evaluate(filter, null, Date.now());
@@ -158,7 +158,7 @@ function query(resource, filter, options, callback) {
}); });
} }
function count(resource, filter) { export function count(resource, filter) {
let q; let q;
filter = expression.evaluate(filter, null, Date.now()); filter = expression.evaluate(filter, null, Date.now());
@@ -187,15 +187,10 @@ function count(resource, filter) {
}); });
} }
function disconnect() { export function disconnect() {
if (_clientPromise) { if (_clientPromise) {
_clientPromise.then(client => { _clientPromise.then(client => {
client.close(); client.close();
}); });
} }
} }
exports.query = query;
exports.count = count;
exports.cache = cache;
exports.disconnect = disconnect;
+25 -43
View File
@@ -1,10 +1,10 @@
"use strict"; "use strict";
const mongodb = require("mongodb"); import { ObjectID } from "mongodb";
const expressionParser = require("../common/expression-parser"); import { map, parse, stringify } from "../common/expression-parser";
const expression = require("../common/expression"); import { likePatternToRegExp } from "../common/expression";
function processDeviceFilter(filter) { export function processDeviceFilter(filter) {
return expressionParser.map(filter, exp => { return map(filter, exp => {
if (!Array.isArray(exp)) return exp; if (!Array.isArray(exp)) return exp;
if (exp[0] === "PARAM") { if (exp[0] === "PARAM") {
@@ -32,7 +32,7 @@ function processDeviceFilter(filter) {
} else if ( } else if (
["=", "<>", ">", ">=", "<", "<=", "LIKE", "NOT LIKE"].includes(exp[0]) ["=", "<>", ">", ">=", "<", "<=", "LIKE", "NOT LIKE"].includes(exp[0])
) { ) {
let e = expressionParser.map(exp, ee => { let e = map(exp, ee => {
if ( if (
Array.isArray(ee) && Array.isArray(ee) &&
ee[0] === "PARAM" && ee[0] === "PARAM" &&
@@ -54,13 +54,12 @@ function processDeviceFilter(filter) {
}); });
} }
function processTasksFilter(filter) { export function processTasksFilter(filter) {
return expressionParser.map(filter, exp => { return map(filter, exp => {
if (!Array.isArray(exp)) return exp; if (!Array.isArray(exp)) return exp;
if (["=", "<>", ">", ">=", "<", "<="].includes(exp[0])) { if (["=", "<>", ">", ">=", "<", "<="].includes(exp[0])) {
const e = exp.slice(); const e = exp.slice();
if (e[1][0] === "PARAM" && e[1][1] === "_id") if (e[1][0] === "PARAM" && e[1][1] === "_id") e[2] = new ObjectID(e[2]);
e[2] = new mongodb.ObjectID(e[2]);
else if (e[1][0] === "PARAM" && e[1][1] === "timestamp") else if (e[1][0] === "PARAM" && e[1][1] === "timestamp")
e[2] = new Date(e[2]); e[2] = new Date(e[2]);
else if (e[1][0] === "PARAM" && e[1][1] === "expiry") else if (e[1][0] === "PARAM" && e[1][1] === "expiry")
@@ -71,8 +70,8 @@ function processTasksFilter(filter) {
}); });
} }
function processFaultsFilter(filter) { export function processFaultsFilter(filter) {
return expressionParser.map(filter, exp => { return map(filter, exp => {
if (!Array.isArray(exp)) return exp; if (!Array.isArray(exp)) return exp;
if (["=", "<>", ">", ">=", "<", "<="].includes(exp[0])) { if (["=", "<>", ">", ">=", "<", "<="].includes(exp[0])) {
const e = exp.slice(); const e = exp.slice();
@@ -85,7 +84,7 @@ function processFaultsFilter(filter) {
}); });
} }
function filterToMongoQuery(filter, negate = false, res = {}) { export function filterToMongoQuery(filter, negate = false, res = {}) {
const op = filter[0]; const op = filter[0];
if ((!negate && op === "AND") || (negate && op === "OR")) { if ((!negate && op === "AND") || (negate && op === "OR")) {
@@ -160,7 +159,7 @@ function filterToMongoQuery(filter, negate = false, res = {}) {
param = filter[1][1]; param = filter[1][1];
flags = ""; flags = "";
} }
const r = expression.likePatternToRegExp(filter[2], filter[3], flags); const r = likePatternToRegExp(filter[2], filter[3], flags);
if (negate) res[param] = { $not: r }; if (negate) res[param] = { $not: r };
else res[param] = r; else res[param] = r;
} else { } else {
@@ -170,7 +169,7 @@ function filterToMongoQuery(filter, negate = false, res = {}) {
return res; return res;
} }
function processDeviceProjection(projection) { export function processDeviceProjection(projection) {
if (!projection) return projection; if (!projection) return projection;
const p = {}; const p = {};
for (const [k, v] of Object.entries(projection)) { for (const [k, v] of Object.entries(projection)) {
@@ -196,7 +195,7 @@ function processDeviceProjection(projection) {
return p; return p;
} }
function processDeviceSort(sort) { export function processDeviceSort(sort) {
if (!sort) return sort; if (!sort) return sort;
const s = {}; const s = {};
for (const [k, v] of Object.entries(sort)) { for (const [k, v] of Object.entries(sort)) {
@@ -217,7 +216,7 @@ function parseDate(d) {
return isNaN(n) ? "" + d : n; return isNaN(n) ? "" + d : n;
} }
function flattenDevice(device) { export function flattenDevice(device) {
function recursive(input, root, output, timestamp) { function recursive(input, root, output, timestamp) {
for (const [name, tree] of Object.entries(input)) { for (const [name, tree] of Object.entries(input)) {
if (!root) { if (!root) {
@@ -361,14 +360,14 @@ function flattenDevice(device) {
return newDevice; return newDevice;
} }
function flattenFault(fault) { export function flattenFault(fault) {
const f = Object.assign({}, fault); const f = Object.assign({}, fault);
if (f.timestamp) f.timestamp = +f.timestamp; if (f.timestamp) f.timestamp = +f.timestamp;
if (f.expiry) f.expiry = +f.expiry; if (f.expiry) f.expiry = +f.expiry;
return f; return f;
} }
function flattenTask(task) { export function flattenTask(task) {
const t = Object.assign({}, task); const t = Object.assign({}, task);
t._id = "" + t._id; t._id = "" + t._id;
if (t.timestamp) t.timestamp = +t.timestamp; if (t.timestamp) t.timestamp = +t.timestamp;
@@ -443,13 +442,11 @@ function mongoQueryToFilter(query) {
return expressions; return expressions;
} }
function flattenPreset(preset) { export function flattenPreset(preset) {
const p = Object.assign({}, preset); const p = Object.assign({}, preset);
if (p.precondition) { if (p.precondition) {
p.precondition = mongoQueryToFilter(JSON.parse(p.precondition)); p.precondition = mongoQueryToFilter(JSON.parse(p.precondition));
p.precondition = p.precondition.length p.precondition = p.precondition.length ? stringify(p.precondition) : "";
? expression.stringify(p.precondition)
: "";
} }
if (p.events) { if (p.events) {
@@ -474,7 +471,7 @@ function flattenPreset(preset) {
return p; return p;
} }
function flattenFile(file) { export function flattenFile(file) {
const f = {}; const f = {};
f["_id"] = file["_id"]; f["_id"] = file["_id"];
if (file.metadata) { if (file.metadata) {
@@ -486,10 +483,10 @@ function flattenFile(file) {
return f; return f;
} }
function preProcessPreset(data) { export function preProcessPreset(data) {
const preset = Object.assign({}, data); const preset = Object.assign({}, data);
preset.precondition = preset.precondition preset.precondition = preset.precondition
? filterToMongoQuery(expression.parse(preset.precondition)) ? filterToMongoQuery(parse(preset.precondition))
: {}; : {};
preset.precondition = JSON.stringify(preset.precondition); preset.precondition = JSON.stringify(preset.precondition);
@@ -526,25 +523,10 @@ function preProcessPreset(data) {
return preset; return preset;
} }
function preProcessProvision(data) { export function preProcessProvision(data) {
return data.script; return data.script;
} }
function preProcessVirtualParameters(data) { export function preProcessVirtualParameters(data) {
return data.script; return data.script;
} }
exports.processDeviceFilter = processDeviceFilter;
exports.processTasksFilter = processTasksFilter;
exports.processFaultsFilter = processFaultsFilter;
exports.filterToMongoQuery = filterToMongoQuery;
exports.processDeviceProjection = processDeviceProjection;
exports.processDeviceSort = processDeviceSort;
exports.flattenDevice = flattenDevice;
exports.flattenFault = flattenFault;
exports.flattenTask = flattenTask;
exports.flattenPreset = flattenPreset;
exports.flattenFile = flattenFile;
exports.preProcessPreset = preProcessPreset;
exports.preProcessProvision = preProcessProvision;
exports.preProcessVirtualParameters = preProcessVirtualParameters;
+1 -3
View File
@@ -21,7 +21,7 @@
const NONEXISTENT = new Object(); const NONEXISTENT = new Object();
const UNDEFINED = undefined; const UNDEFINED = undefined;
class VersionedMap { export default class VersionedMap {
constructor() { constructor() {
this._sizeDiff = [0]; this._sizeDiff = [0];
this._revision = 0; this._revision = 0;
@@ -189,5 +189,3 @@ class VersionedMap {
} }
} }
} }
module.exports = VersionedMap;
+7 -3
View File
@@ -38,12 +38,16 @@
"codemirror": "^5.40.0", "codemirror": "^5.40.0",
"cssnano": "^4.0.5", "cssnano": "^4.0.5",
"eslint": "^5.10.0", "eslint": "^5.10.0",
"esm": "^3.1.1",
"mithril": "^1.1.6", "mithril": "^1.1.6",
"postcss-cssnext": "^3.1.0", "postcss-cssnext": "^3.1.0",
"postcss-import": "^11.1.0", "postcss-import": "^11.1.0",
"prettier": "^1.15.3", "prettier": "^1.15.3",
"rollup": "^1.1.2",
"rollup-plugin-json": "^3.1.0",
"rollup-plugin-replace": "^2.1.0",
"rollup-plugin-terser": "^4.0.2",
"webpack": "^4.16.3", "webpack": "^4.16.3",
"webpack-node-externals": "^1.7.2",
"yaml": "^1.0.0" "yaml": "^1.0.0"
}, },
"engines": { "engines": {
@@ -53,8 +57,8 @@
"compile": "echo 'Compile step no longer required, skipping.'", "compile": "echo 'Compile step no longer required, skipping.'",
"configure": "if [ ! -f ./config/config.json ]; then cp ./config/config-sample.json ./config/config.json; fi", "configure": "if [ ! -f ./config/config.json ]; then cp ./config/config-sample.json ./config/config.json; fi",
"install": "npm run configure", "install": "npm run configure",
"test": "ava --verbose --color", "test": "ava -r esm --verbose --color",
"lint": "eslint --fix lib ui build test bin/*; prettier --write \"lib/**/*.js\" \"ui/**/*.js\" \"build/**/*.js\" \"test/**/*.js\" bin/*", "lint": "eslint --fix lib ui build test bin/* tools/*; prettier --parser babylon --write \"lib/**/*.js\" \"ui/**/*.js\" \"build/**/*.js\" \"test/**/*.js\" bin/* tools/*",
"build": "node build/build" "build": "node build/build"
} }
} }
+2 -2
View File
@@ -1,7 +1,7 @@
"use strict"; "use strict";
const ava = require("ava"); import ava from "ava";
const common = require("../lib/common.js"); import * as common from "../lib/common.js";
ava("typeOf", t => { ava("typeOf", t => {
const cases = [ const cases = [
+5 -5
View File
@@ -1,8 +1,8 @@
#!/usr/bin/env node #!/usr/bin/env -S node -r esm
"use strict"; "use strict";
const mongodb = require("mongodb"); import { MongoClient } from "mongodb";
const config = require("../lib/config"); import { get } from "../lib/config";
const configData = { const configData = {
"auth.simple.users.admin.password": "'admin'", "auth.simple.users.admin.password": "'admin'",
@@ -158,8 +158,8 @@ const configData = {
}; };
async function run() { async function run() {
const client = await mongodb.MongoClient.connect( const client = await MongoClient.connect(
config.get("MONGODB_CONNECTION_URL"), get("MONGODB_CONNECTION_URL"),
{ useNewUrlParser: true } { useNewUrlParser: true }
); );
const collection = client.db().collection("config"); const collection = client.db().collection("config");
+31 -38
View File
@@ -1,4 +1,4 @@
#!/usr/bin/env node #!/usr/bin/env -S node -r esm
/** /**
* Copyright 2013-2017 Zaid Abdulla * Copyright 2013-2017 Zaid Abdulla
* *
@@ -19,73 +19,66 @@
*/ */
"use strict"; "use strict";
const fs = require("fs"); import fs from "fs";
const path = require("path"); import path from "path";
import { connect, devicesCollection } from "../lib/db";
const outputFilename = path.resolve(`data_model_${process.argv[2]}.json`); const outputFilename = path.resolve(`data_model_${process.argv[2]}.json`);
process.chdir(path.resolve(path.dirname(fs.realpathSync(__filename)), "..")); process.chdir(path.resolve(path.dirname(fs.realpathSync(__filename)), ".."));
const db = require("../lib/db");
const dataModel = {}; const dataModel = {};
function recursive(object, p) {
if (!p) p = "";
function recursive(object, path) { for (const key in object) {
if (!path) if (key[0] === "_" || !object[key]) continue;
path = "";
for (let key in object) { const isObject = object[key]["_object"];
if (key[0] === "_" || !object[key]) const isValue = "_value" in object[key];
continue;
let isObject = object[key]["_object"];
let isValue = "_value" in object[key];
if ((isObject && isValue) || (!isObject && !isValue)) if ((isObject && isValue) || (!isObject && !isValue))
throw new Error("Unexpected object"); throw new Error("Unexpected object");
if (isValue) { if (isValue) {
let value = object[key]["_value"]; let value = object[key]["_value"];
if (typeof value === "boolean") if (typeof value === "boolean") value = +value;
value = +value;
let values = [object[key]["_writable"] || false, value.toString()]; const values = [object[key]["_writable"] || false, value.toString()];
if (object[key]["_type"]) if (object[key]["_type"]) values.push(object[key]["_type"]);
values.push(object[key]["_type"]);
dataModel[`${path}${key}`] = values; dataModel[`${p}${key}`] = values;
} }
if (isObject) { if (isObject) {
let values = [object[key]["_writable"] || false]; const values = [object[key]["_writable"] || false];
dataModel[`${path}${key}.`] = values; dataModel[`${p}${key}.`] = values;
recursive(object[key], `${path}${key}.`); recursive(object[key], `${p}${key}.`);
} }
} }
} }
connect(err => {
if (err) throw err;
db.connect(function(err) { return devicesCollection.findOne(
if (err) {
throw err;
return db.devicesCollection.findOne({
_id: process.argv[2] _id: process.argv[2]
}, {}, function(err, device) { },
if (err) {},
throw err; (err, device) => {
if (err) throw err;
if (!device) if (!device) throw new Error("Device not found");
throw new Error("Device not found");
recursive(device); recursive(device);
fs.writeFile(outputFilename, JSON.stringify(dataModel, null, 2), function() { fs.writeFile(outputFilename, JSON.stringify(dataModel, null, 2), () => {
if (err) if (err) throw err;
throw err;
console.log(`${outputFilename} saved`); process.stdout.write(`${outputFilename} saved\n`);
process.exit(0); process.exit(0);
}); });
}); }
);
}); });
+1 -2
View File
@@ -5,7 +5,6 @@
}, },
"parser": "babel-eslint", "parser": "babel-eslint",
"parserOptions": { "parserOptions": {
"allowImportExportEverywhere": true, "allowImportExportEverywhere": true
"sourceType": "module"
} }
} }
+19 -16
View File
@@ -13,20 +13,23 @@ import tags from "./components/tags";
import ping from "./components/ping"; import ping from "./components/ping";
import deviceLink from "./components/device-link"; import deviceLink from "./components/device-link";
function get(name) { const comps = {
if (name === "parameter") return parameter; parameter,
else if (name === "parameter-list") return parameterList; "parameter-list": parameterList,
else if (name === "parameter-table") return parameterTable; "parameter-table": parameterTable,
else if (name === "overview-dot") return overviewDot; "overview-dot": overviewDot,
else if (name === "container") return container; container,
else if (name === "summon-button") return summonButton; "summon-button": summonButton,
else if (name === "device-faults") return deviceFaults; "device-faults": deviceFaults,
else if (name === "all-parameters") return allParameters; "all-parameters": allParameters,
else if (name === "device-actions") return deviceActions; "device-actions": deviceActions,
else if (name === "tags") return tags; tags,
else if (name === "ping") return ping; ping,
else if (name === "device-link") return deviceLink; "device-link": deviceLink
else throw new Error(`No such component '${name}'`); };
}
export { get }; export function get(name) {
const c = comps[name];
if (!c) throw new Error(`No such component '${name}'`);
return c;
}
+2 -2
View File
@@ -4,10 +4,10 @@ import m from "mithril";
import * as components from "../components"; import * as components from "../components";
import * as taskQueue from "../task-queue"; import * as taskQueue from "../task-queue";
import longTextComponent from "../long-text-component"; import longTextComponent from "../long-text-component";
import * as expression from "../../lib/common/expression"; import { parse } from "../../lib/common/expression-parser";
import memoize from "../../lib/common/memoize"; import memoize from "../../lib/common/memoize";
const memoizedParse = memoize(expression.parse); const memoizedParse = memoize(parse);
function escapeRegExp(str) { function escapeRegExp(str) {
return str.replace(/[-[\]/{}()*+?.\\^$|]/g, "\\$&"); return str.replace(/[-[\]/{}()*+?.\\^$|]/g, "\\$&");
+3 -5
View File
@@ -1,20 +1,20 @@
"use strict"; "use strict";
import m from "mithril"; import m from "mithril";
import * as components from "../components"; import { get } from "../components";
import memoize from "../../lib/common/memoize"; import memoize from "../../lib/common/memoize";
const getChildAttrs = memoize((attrs, device) => const getChildAttrs = memoize((attrs, device) =>
Object.assign({}, attrs, { device: device }) Object.assign({}, attrs, { device: device })
); );
const component = { export default {
view: vnode => { view: vnode => {
const device = vnode.attrs.device; const device = vnode.attrs.device;
const rows = Object.values(vnode.attrs.parameters).map(parameter => { const rows = Object.values(vnode.attrs.parameters).map(parameter => {
const p = m( const p = m(
components.get(parameter.type || "parameter"), get(parameter.type || "parameter"),
getChildAttrs(parameter, device) getChildAttrs(parameter, device)
); );
@@ -33,5 +33,3 @@ const component = {
return m("table.parameter-list", rows); return m("table.parameter-list", rows);
} }
}; };
export default component;
+6 -6
View File
@@ -8,19 +8,20 @@ import * as store from "./store";
import * as components from "./components"; import * as components from "./components";
import * as taskQueue from "./task-queue"; import * as taskQueue from "./task-queue";
import * as notifications from "./notifications"; import * as notifications from "./notifications";
import * as expression from "../lib/common/expression"; import { evaluate, extractParams } from "../lib/common/expression";
import { parse, stringify } from "../lib/common/expression-parser";
import memoize from "../lib/common/memoize"; import memoize from "../lib/common/memoize";
import * as smartQuery from "./smart-query"; import * as smartQuery from "./smart-query";
import * as expressionParser from "../lib/common/expression-parser"; import * as expressionParser from "../lib/common/expression-parser";
const PAGE_SIZE = config.ui.pageSize || 10; const PAGE_SIZE = config.ui.pageSize || 10;
const memoizedParse = memoize(expression.parse); const memoizedParse = memoize(parse);
const memoizedJsonParse = memoize(JSON.parse); const memoizedJsonParse = memoize(JSON.parse);
const memoizedGetSortable = memoize(p => { const memoizedGetSortable = memoize(p => {
const expressionParams = expression.extractParams(p); const expressionParams = extractParams(p);
if (expressionParams.length === 1) { if (expressionParams.length === 1) {
const param = expression.evaluate(expressionParams[0]); const param = evaluate(expressionParams[0]);
if (typeof param === "string") return param; if (typeof param === "string") return param;
} }
return null; return null;
@@ -28,8 +29,7 @@ const memoizedGetSortable = memoize(p => {
const getDownloadUrl = memoize((filter, indexParameters) => { const getDownloadUrl = memoize((filter, indexParameters) => {
const columns = {}; const columns = {};
for (const p of indexParameters) for (const p of indexParameters) columns[p.label] = stringify(p.parameter);
columns[p.label] = expression.stringify(p.parameter);
return `/api/devices.csv?${m.buildQueryString({ return `/api/devices.csv?${m.buildQueryString({
filter: filter, filter: filter,
columns: JSON.stringify(columns) columns: JSON.stringify(columns)
+3 -4
View File
@@ -5,16 +5,15 @@ import config from "./config";
import filterComponent from "./filter-component"; import filterComponent from "./filter-component";
import * as store from "./store"; import * as store from "./store";
import * as notifications from "./notifications"; import * as notifications from "./notifications";
import * as expression from "../lib/common/expression";
import memoize from "../lib/common/memoize"; import memoize from "../lib/common/memoize";
import * as smartQuery from "./smart-query"; import * as smartQuery from "./smart-query";
import * as expressionParser from "../lib/common/expression-parser"; import { map, parse } from "../lib/common/expression-parser";
import { loadYaml, yaml } from "./dynamic-loader"; import { loadYaml, yaml } from "./dynamic-loader";
import longTextComponent from "./long-text-component"; import longTextComponent from "./long-text-component";
const PAGE_SIZE = config.ui.pageSize || 10; const PAGE_SIZE = config.ui.pageSize || 10;
const memoizedParse = memoize(expression.parse); const memoizedParse = memoize(parse);
const memoizedJsonParse = memoize(JSON.parse); const memoizedJsonParse = memoize(JSON.parse);
const attributes = [ const attributes = [
@@ -41,7 +40,7 @@ const getDownloadUrl = memoize(filter => {
}); });
const unpackSmartQuery = memoize(query => { const unpackSmartQuery = memoize(query => {
return expressionParser.map(query, e => { return map(query, e => {
if (Array.isArray(e) && e[0] === "FUNC" && e[1] === "Q") if (Array.isArray(e) && e[0] === "FUNC" && e[1] === "Q")
return smartQuery.unpack("faults", e[2], e[3]); return smartQuery.unpack("faults", e[2], e[3]);
return e; return e;
+3 -4
View File
@@ -5,17 +5,16 @@ import m from "mithril";
import config from "./config"; import config from "./config";
import filterComponent from "./filter-component"; import filterComponent from "./filter-component";
import * as store from "./store"; import * as store from "./store";
import * as expression from "../lib/common/expression";
import * as notifications from "./notifications"; import * as notifications from "./notifications";
import memoize from "../lib/common/memoize"; import memoize from "../lib/common/memoize";
import putFormComponent from "./put-form-component"; import putFormComponent from "./put-form-component";
import * as overlay from "./overlay"; import * as overlay from "./overlay";
import * as smartQuery from "./smart-query"; import * as smartQuery from "./smart-query";
import * as expressionParser from "../lib/common/expression-parser"; import { map, parse } from "../lib/common/expression-parser";
const PAGE_SIZE = config.ui.pageSize || 10; const PAGE_SIZE = config.ui.pageSize || 10;
const memoizedParse = memoize(expression.parse); const memoizedParse = memoize(parse);
const memoizedJsonParse = memoize(JSON.parse); const memoizedJsonParse = memoize(JSON.parse);
const attributes = [ const attributes = [
@@ -45,7 +44,7 @@ const formData = {
}; };
const unpackSmartQuery = memoize(query => { const unpackSmartQuery = memoize(query => {
return expressionParser.map(query, e => { return map(query, e => {
if (Array.isArray(e) && e[0] === "FUNC" && e[1] === "Q") if (Array.isArray(e) && e[0] === "FUNC" && e[1] === "Q")
return smartQuery.unpack("files", e[2], e[3]); return smartQuery.unpack("files", e[2], e[3]);
return e; return e;
+5 -5
View File
@@ -1,7 +1,7 @@
"use strict"; "use strict";
import m from "mithril"; import m from "mithril";
import * as expression from "../lib/common/expression"; import { parse, stringify } from "../lib/common/expression-parser";
import memoize from "../lib/common/memoize"; import memoize from "../lib/common/memoize";
import Autocomplete from "./autocomplete-compnent"; import Autocomplete from "./autocomplete-compnent";
import * as smartQuery from "./smart-query"; import * as smartQuery from "./smart-query";
@@ -21,19 +21,19 @@ function parseFilter(f) {
const v = f.slice(k.length + 1).trim(); const v = f.slice(k.length + 1).trim();
return ["FUNC", "Q", k.trim(), v]; return ["FUNC", "Q", k.trim(), v];
} }
return expression.parse(f); return parse(f);
} }
function stringifyFilter(f) { function stringifyFilter(f) {
if (Array.isArray(f) && f[0] === "FUNC" && f[1] === "Q") if (Array.isArray(f) && f[0] === "FUNC" && f[1] === "Q")
return `${f[2]}: ${f[3]}`; return `${f[2]}: ${f[3]}`;
return expression.stringify(f); return stringify(f);
} }
const splitFilter = memoize(filter => { const splitFilter = memoize(filter => {
if (!filter) return [""]; if (!filter) return [""];
const list = []; const list = [];
const f = expression.parse(filter); const f = parse(filter);
if (Array.isArray(f) && f[0] === "AND") if (Array.isArray(f) && f[0] === "AND")
for (const ff of f.slice(1)) list.push(stringifyFilter(ff)); for (const ff of f.slice(1)) list.push(stringifyFilter(ff));
else list.push(stringifyFilter(f)); else list.push(stringifyFilter(f));
@@ -70,7 +70,7 @@ const component = {
} else { } else {
if (filter.length > 1) filter = ["AND"].concat(filter); if (filter.length > 1) filter = ["AND"].concat(filter);
else filter = filter[0]; else filter = filter[0];
vnode.attrs.onChange(expression.stringify(filter)); vnode.attrs.onChange(stringify(filter));
} }
} }
} }
+2 -2
View File
@@ -1,10 +1,10 @@
"use strict"; "use strict";
import m from "mithril"; import m from "mithril";
import * as expression from "../lib/common/expression"; import { stringify } from "../lib/common/expression-parser";
import memoize from "../lib/common/memoize"; import memoize from "../lib/common/memoize";
const memoizedStringify = memoize(expression.stringify); const memoizedStringify = memoize(stringify);
const drawChart = function(chartData) { const drawChart = function(chartData) {
const slices = chartData.slices; const slices = chartData.slices;
+3 -4
View File
@@ -6,16 +6,15 @@ import config from "./config";
import filterComponent from "./filter-component"; import filterComponent from "./filter-component";
import * as overlay from "./overlay"; import * as overlay from "./overlay";
import * as store from "./store"; import * as store from "./store";
import * as expression from "../lib/common/expression";
import * as notifications from "./notifications"; import * as notifications from "./notifications";
import putFormComponent from "./put-form-component"; import putFormComponent from "./put-form-component";
import memoize from "../lib/common/memoize"; import memoize from "../lib/common/memoize";
import * as smartQuery from "./smart-query"; import * as smartQuery from "./smart-query";
import * as expressionParser from "../lib/common/expression-parser"; import { map, parse } from "../lib/common/expression-parser";
const PAGE_SIZE = config.ui.pageSize || 10; const PAGE_SIZE = config.ui.pageSize || 10;
const memoizedParse = memoize(expression.parse); const memoizedParse = memoize(parse);
const memoizedJsonParse = memoize(JSON.parse); const memoizedJsonParse = memoize(JSON.parse);
const attributes = [ const attributes = [
@@ -30,7 +29,7 @@ const attributes = [
]; ];
const unpackSmartQuery = memoize(query => { const unpackSmartQuery = memoize(query => {
return expressionParser.map(query, e => { return map(query, e => {
if (Array.isArray(e) && e[0] === "FUNC" && e[1] === "Q") if (Array.isArray(e) && e[0] === "FUNC" && e[1] === "Q")
return smartQuery.unpack("presets", e[2], e[3]); return smartQuery.unpack("presets", e[2], e[3]);
return e; return e;
+3 -4
View File
@@ -5,18 +5,17 @@ import m from "mithril";
import config from "./config"; import config from "./config";
import filterComponent from "./filter-component"; import filterComponent from "./filter-component";
import * as store from "./store"; import * as store from "./store";
import * as expression from "../lib/common/expression";
import * as notifications from "./notifications"; import * as notifications from "./notifications";
import memoize from "../lib/common/memoize"; import memoize from "../lib/common/memoize";
import putFormComponent from "./put-form-component"; import putFormComponent from "./put-form-component";
import * as overlay from "./overlay"; import * as overlay from "./overlay";
import * as smartQuery from "./smart-query"; import * as smartQuery from "./smart-query";
import * as expressionParser from "../lib/common/expression-parser"; import { map, parse } from "../lib/common/expression-parser";
import { loadCodeMirror } from "./dynamic-loader"; import { loadCodeMirror } from "./dynamic-loader";
const PAGE_SIZE = config.ui.pageSize || 10; const PAGE_SIZE = config.ui.pageSize || 10;
const memoizedParse = memoize(expression.parse); const memoizedParse = memoize(parse);
const memoizedJsonParse = memoize(JSON.parse); const memoizedJsonParse = memoize(JSON.parse);
const attributes = [ const attributes = [
@@ -25,7 +24,7 @@ const attributes = [
]; ];
const unpackSmartQuery = memoize(query => { const unpackSmartQuery = memoize(query => {
return expressionParser.map(query, e => { return map(query, e => {
if (Array.isArray(e) && e[0] === "FUNC" && e[1] === "Q") if (Array.isArray(e) && e[0] === "FUNC" && e[1] === "Q")
return smartQuery.unpack("provisions", e[2], e[3]); return smartQuery.unpack("provisions", e[2], e[3]);
return e; return e;
+24 -36
View File
@@ -1,12 +1,12 @@
"use strict"; "use strict";
import m from "mithril"; import m from "mithril";
import * as expressionParser from "../lib/common/expression-parser"; import { map, stringify } from "../lib/common/expression-parser";
import * as expression from "../lib/common/expression"; import { or, and, not, evaluate, subset } from "../lib/common/expression";
import memoize from "../lib/common/memoize"; import memoize from "../lib/common/memoize";
const memoizedStringify = memoize(expression.stringify); const memoizedStringify = memoize(stringify);
const memoizedEvaluate = memoize(expression.evaluate); const memoizedEvaluate = memoize(evaluate);
let fulfillTimestamp = 0; let fulfillTimestamp = 0;
@@ -74,43 +74,37 @@ function limitFilter(filter, sort, bookmark) {
const arr = Object.entries(sort) const arr = Object.entries(sort)
.sort(sortSort) .sort(sortSort)
.reverse(); .reverse();
return expression.and( return and(
filter, filter,
arr.reduce((cur, kv) => { arr.reduce((cur, kv) => {
const [param, asc] = kv; const [param, asc] = kv;
if (asc <= 0) { if (asc <= 0) {
if (bookmark[param] == null) { if (bookmark[param] == null) {
return expression.or( return or(
["IS NOT NULL", ["PARAM", param]], ["IS NOT NULL", ["PARAM", param]],
expression.and(["IS NULL", ["PARAM", param]], cur) and(["IS NULL", ["PARAM", param]], cur)
); );
} }
let f = null; let f = null;
if (typeof bookmark[param] !== "string") if (typeof bookmark[param] !== "string")
f = expression.or(f, [">=", ["PARAM", param], ""]); f = or(f, [">=", ["PARAM", param], ""]);
f = expression.or(f, [">", ["PARAM", param], bookmark[param]]); f = or(f, [">", ["PARAM", param], bookmark[param]]);
return expression.or( return or(f, and(["=", ["PARAM", param], bookmark[param]], cur));
f,
expression.and(["=", ["PARAM", param], bookmark[param]], cur)
);
} else { } else {
let f = ["IS NULL", ["PARAM", param]]; let f = ["IS NULL", ["PARAM", param]];
if (bookmark[param] == null) return expression.and(f, cur); if (bookmark[param] == null) return and(f, cur);
if (typeof bookmark[param] !== "number") { if (typeof bookmark[param] !== "number") {
f = expression.or(f, [">=", ["PARAM", param], 0]); f = or(f, [">=", ["PARAM", param], 0]);
f = expression.or(f, ["<", ["PARAM", param], 0]); f = or(f, ["<", ["PARAM", param], 0]);
} }
f = expression.or(f, ["<", ["PARAM", param], bookmark[param]]); f = or(f, ["<", ["PARAM", param], bookmark[param]]);
return expression.or( return or(f, and(["=", ["PARAM", param], bookmark[param]], cur));
f,
expression.and(["=", ["PARAM", param], bookmark[param]], cur)
);
} }
}, true) }, true)
); );
@@ -157,7 +151,7 @@ function compareFunction(sort) {
function findMatches(resourceType, filter, sort, limit) { function findMatches(resourceType, filter, sort, limit) {
// Handle "tag =" and "tag <>" special cases // Handle "tag =" and "tag <>" special cases
if (resourceType === "devices") { if (resourceType === "devices") {
filter = expressionParser.map(filter, e => { filter = map(filter, e => {
if ( if (
Array.isArray(e) && Array.isArray(e) &&
Array.isArray(e[1]) && Array.isArray(e[1]) &&
@@ -173,7 +167,7 @@ function findMatches(resourceType, filter, sort, limit) {
let value = []; let value = [];
for (const obj of resources[resourceType].objects.values()) for (const obj of resources[resourceType].objects.values())
if (expression.evaluate(filter, obj, fulfillTimestamp)) value.push(obj); if (evaluate(filter, obj, fulfillTimestamp)) value.push(obj);
value = value.sort(compareFunction(sort)); value = value.sort(compareFunction(sort));
if (limit) value = value.slice(0, limit); if (limit) value = value.slice(0, limit);
@@ -191,7 +185,7 @@ function inferQuery(resourceType, queryResponse) {
if (bookmark) filter = limitFilter(filter, sort, bookmark); if (bookmark) filter = limitFilter(filter, sort, bookmark);
if ( if (
resources[resourceType].combinedFilter && resources[resourceType].combinedFilter &&
expression.subset(filter, resources[resourceType].combinedFilter) subset(filter, resources[resourceType].combinedFilter)
) )
queries.fulfilled.set(queryResponse, fulfillTimestamp); queries.fulfilled.set(queryResponse, fulfillTimestamp);
} }
@@ -356,7 +350,7 @@ function fulfill(accessTimestamp, _fulfillTimestamp) {
if ( if (
resources[resourceType].combinedFilter && resources[resourceType].combinedFilter &&
expression.subset(filter, resources[resourceType].combinedFilter) subset(filter, resources[resourceType].combinedFilter)
) { ) {
queries.value.set( queries.value.set(
queryResponse, queryResponse,
@@ -367,7 +361,7 @@ function fulfill(accessTimestamp, _fulfillTimestamp) {
return false; return false;
} }
combinedFilter = expression.or(combinedFilter, filter); combinedFilter = or(combinedFilter, filter);
return true; return true;
}); });
@@ -379,12 +373,12 @@ function fulfill(accessTimestamp, _fulfillTimestamp) {
deleted = new Set(resources[resourceType].objects.keys()); deleted = new Set(resources[resourceType].objects.keys());
let combinedFilterDiff = combinedFilter; let combinedFilterDiff = combinedFilter;
if (resources[resourceType].combinedFilter) { if (resources[resourceType].combinedFilter) {
combinedFilterDiff = expression.and( combinedFilterDiff = and(
combinedFilterDiff, combinedFilterDiff,
expression.not(resources[resourceType].combinedFilter) not(resources[resourceType].combinedFilter)
); );
} }
resources[resourceType].combinedFilter = expression.or( resources[resourceType].combinedFilter = or(
combinedFilter, combinedFilter,
resources[resourceType].combinedFilter resources[resourceType].combinedFilter
); );
@@ -411,13 +405,7 @@ function fulfill(accessTimestamp, _fulfillTimestamp) {
for (const d of deleted) { for (const d of deleted) {
const obj = resources[resourceType].objects.get(d); const obj = resources[resourceType].objects.get(d);
if ( if (evaluate(combinedFilterDiff, obj, fulfillTimestamp))
expression.evaluate(
combinedFilterDiff,
obj,
fulfillTimestamp
)
)
resources[resourceType].objects.delete(d); resources[resourceType].objects.delete(d);
} }
+3 -4
View File
@@ -5,18 +5,17 @@ import m from "mithril";
import config from "./config"; import config from "./config";
import filterComponent from "./filter-component"; import filterComponent from "./filter-component";
import * as store from "./store"; import * as store from "./store";
import * as expression from "../lib/common/expression";
import * as notifications from "./notifications"; import * as notifications from "./notifications";
import memoize from "../lib/common/memoize"; import memoize from "../lib/common/memoize";
import putFormComponent from "./put-form-component"; import putFormComponent from "./put-form-component";
import * as overlay from "./overlay"; import * as overlay from "./overlay";
import * as smartQuery from "./smart-query"; import * as smartQuery from "./smart-query";
import * as expressionParser from "../lib/common/expression-parser"; import { map, parse } from "../lib/common/expression-parser";
import { loadCodeMirror } from "./dynamic-loader"; import { loadCodeMirror } from "./dynamic-loader";
const PAGE_SIZE = config.ui.pageSize || 10; const PAGE_SIZE = config.ui.pageSize || 10;
const memoizedParse = memoize(expression.parse); const memoizedParse = memoize(parse);
const memoizedJsonParse = memoize(JSON.parse); const memoizedJsonParse = memoize(JSON.parse);
const attributes = [ const attributes = [
@@ -25,7 +24,7 @@ const attributes = [
]; ];
const unpackSmartQuery = memoize(query => { const unpackSmartQuery = memoize(query => {
return expressionParser.map(query, e => { return map(query, e => {
if (Array.isArray(e) && e[0] === "FUNC" && e[1] === "Q") if (Array.isArray(e) && e[0] === "FUNC" && e[1] === "Q")
return smartQuery.unpack("virtualParameters", e[2], e[3]); return smartQuery.unpack("virtualParameters", e[2], e[3]);
return e; return e;