UNPKG

@sassoftware/esp-connect

Version:

Package used to connect to an ESP server (version 6.2+)

469 lines (386 loc) 11.5 kB
/* Copyright © 2020, SAS Institute Inc., Cary, NC, USA. All Rights Reserved. SPDX-License-Identifier: Apache-2.0 */ import {serverconn} from "./serverconn.js"; import {tools} from "./tools.js"; import {xpath} from "./xpath.js"; import {Options} from "./options.js"; class Router { constructor() { this._servers = {}; this._connections = {}; this._destinations = {}; this._routes = {}; this._models = {}; this._errors = {}; } configure(config) { var xml = xpath.createXml(config); xpath.getNodes("//esp-engines/esp-engine",xml).forEach((node) => { var name = node.getAttribute("name"); var url = "http://" + node.getAttribute("host") + ":" + node.getAttribute("port"); this.addServer(name,url); }); xpath.getNodes("//esp-destinations/*",xml).forEach((node) => { if (node.tagName == "publish-destination") { var name = node.getAttribute("name"); var target = xpath.getNode("publish-target",node); if (target == null) { throw("no target for publish destination"); } var destination = new PublishDestination(this); var n; destination.opcode = node.getAttribute("opcode"); if ((n = xpath.getNode("engine-func",target)) != null) { destination.getEngine = new Function("data",xpath.nodeText(n)); } else { throw("you must supply the engine-func element"); } if ((n = xpath.getNode("project-func",target)) != null) { destination.getProject = new Function("data",xpath.nodeText(n)); } else { throw("you must supply the project-func element"); } if ((n = xpath.getNode("contquery-func",target)) != null) { destination.getContquery = new Function("data",xpath.nodeText(n)); } else { throw("you must supply the contquery-func element"); } if ((n = xpath.getNode("window-func",target)) != null) { destination.getWindow = new Function("data",xpath.nodeText(n)); } else { throw("you must supply the window-func element"); } this._destinations[name] = destination; } }); this._routes = {}; xpath.getNodes("//esp-routes/esp-route",xml).forEach((node) => { var name = node.getAttribute("name"); var route = new Route(name,this); route.to = node.getAttribute("to"); var n; if ((n = xpath.getNode("engine-expr",node)) != null) { route.engine = xpath.nodeText(n); } if ((n = xpath.getNode("project-expr",node)) != null) { route.project = xpath.nodeText(n); } if ((n = xpath.getNode("contquery-expr",node)) != null) { route.contquery = xpath.nodeText(n); } if ((n = xpath.getNode("window-expr",node)) != null) { route.win = xpath.nodeText(n); } this._routes[name] = route; }); } addServer(name,url) { this._servers[name] = url; } addDestination(name) { var destination = new Destination(this); this._destinations[name] = destination; return(destination); } addPublishDestination(name) { var destination = new PublishDestination(this); this._destinations[name] = destination; return(destination); } addRoute(name) { var route = new Route(name,this); this._routes[name] = route; return(route); } ready(connection) { this._connections[connection.getOpt("name")] = connection; const self = this; connection.getModel().then( function(result) { self.modelLoaded(result,connection); } ); } error(connection) { var name = connection.getOpt("name"); if (this._errors.hasOwnProperty(name) == false) { console.log("server error " + connection); this._errors[name] = connection; } delete this._models[name]; } getEngine(name) { return(this._connections.hasOwnProperty(name) ? this._connections[name] : null); } getModel(name) { return(this._models.hasOwnProperty(name) ? this._models[name] : null); } modelLoaded(model,connection) { var name = connection.getOpt("name"); this._models[name] = model; if (this._errors.hasOwnProperty(name)) { console.log("server back up " + connection); delete this._errors[name]; } var responses = Object.keys(this._models).length + Object.keys(this._errors).length; if (responses == Object.keys(this._connections).length) { Object.values(this._destinations).forEach((dest) => { dest.init(); }); Object.values(this._routes).forEach((route) => { route.init(); }); } } start() { Object.values(this._connections).forEach((engine) => { engine.close(); }); this._connections = {}; Object.keys(this._servers).forEach((name) => { var url = this._servers[name]; var conn = serverconn.create(url,this,{name:name}); conn.start(); }); } stop() { Object.values(this._connections).forEach((engine) => { engine.close(); }); } } class Destination { constructor(router) { this._router = router; } init() { if (tools.supports(this,"process") == false) { throw("destination must implement the process(data) method"); } } } class PublishDestination extends Destination { constructor(router) { super(router); this._publishers = {}; this._opcode = null; } set opcode(value) { this._opcode = value; } get opcode() { return(this._opcode); } init() { super.init(); ["getEngine","getProject","getContquery","getWindow"].forEach((method) => { if (tools.supports(this,method) == false) { throw("destination must implement the " + method + " method"); } }); this._publishers = {}; } process(data) { var e = this.getEngine(data); if (e == null) { return; } var engine = this._router.getEngine(e); if (engine == null) { console.log("cannot find engine: " + e); return; } var p = this.getProject(data); var cq = this.getContquery(data); var w = this.getWindow(data); var key = e + "/" + p + "/" + cq + "/" + w; var publisher = null; if (this._publishers.hasOwnProperty(key)) { publisher = this._publishers[key]; } else { var id = p + "/" + cq + "/" + w; publisher = engine.getPublisher({window:id,id:id,binary:true}); this._publishers[key] = publisher; } publisher.add(data); publisher.publish(); } } class Route { constructor(name,router) { this._name = name; this._router = router; this._engine = null; this._project = null; this._contquery = null; this._window = null; this._to = null; this._destinations = []; } get name() { return(this._name); } get engine() { return(this._engine); } set engine(value) { this._engine = (value != null) ? new RegExp(value) : null; } get project() { return(this._project); } set project(value) { this._project = (value != null) ? new RegExp(value) : null; } get contquery() { return(this._contquery); } set contquery(value) { this._contquery = (value != null) ? new RegExp(value) : null; } get win() { return(this._window); } set win(value) { this._window = (value != null) ? new RegExp(value) : null; } get to() { return(this._to); } set to(value) { this._to = (value != null) ? value.split(",") : null; } init() { if (this._to == null) { throw("route " + this.name + " does not have any destinations associated with it"); } if (this._engine != null) { Object.keys(this._router._connections).forEach((key) => { if (this._engine.test(key)) { var engine = this._router._connections[key]; var projects = []; var model = this._router.getModel(engine.getOpt("name")); model.projects.forEach((p) => { if (this._project == null || this._project.test(p.name)) { projects.push(p); } }); var contqueries = []; projects.forEach((p) => { p.contqueries.forEach((cq) => { if (this._contquery == null || this._contquery.test(cq.name)) { contqueries.push(cq); } }); }); var windows = []; contqueries.forEach((cq) => { cq.windows.forEach((w) => { if (this._window == null || this._window.test(w.name)) { windows.push(w); } }); }); windows.forEach((w) => { var events = engine.getEventStream({window:w.key,maxevents:0,schema:false}); events.addDelegate(this); }); } }); } this._destinations = []; this._to.forEach((s) => { if (this._router._destinations.hasOwnProperty(s)) { this._destinations.push(this._router._destinations[s]); } }); } dataChanged(datasource,data,clear) { data.forEach((item) => { this._destinations.forEach((dest) => { dest.process(item); }); }); } } export {Router};