monitor: Test page for monitoring
[src/app-framework-binder.git] / test / monitoring / monitor.js
1
2 var afb;
3 var ws;
4
5 var t_api;
6 var t_verb;
7 var t_logmsg;
8 var t_traceevent;
9 var t_verbosity;
10 var t_trace;
11 var apis = {};
12 var events = [];
13 var inhibit = false;
14 var msgs = false;
15
16 var root_node;
17 var connected_node;
18 var trace_events_node;
19 var logmsgs_node;
20 var apis_node;
21 var all_node;
22
23 /* flags */
24 var show_perms = false;
25 var show_monitor_events = false;
26
27 _.templateSettings = { interpolate: /\{\{(.+?)\}\}/g };
28
29 function untrace_all() {
30         do_call("monitor/trace", {drop: true});
31         for_all_nodes(null, ".trace-item input[type=radio]", function(n){n.checked = n.value == "no";});
32 }
33
34 function disconnect() {
35         untrace_all();
36         apis = {};
37         apis_node.innerHTML = "";
38         root_node.className = "off";
39         connected_node.innerHTML = "Connection Closed";
40         connected_node.className = "ok";
41         ws && ws.close();
42         afb = null;
43         ws = null;
44 }
45
46 function connect(args) {
47         drop_all_trace_events();
48         drop_all_logmsgs();
49         ws && ws.close();
50         afb = new AFB(args);
51         ws = new afb.ws(onopen, onabort);
52 }
53
54 function on_connect(evt) {
55         connect({
56                 host: at("param-host").value + ":" + at("param-port").value,
57                 token: at("param-token").value
58         });
59 }
60
61 function init() {
62         /* prepare the DOM templates */
63         t_api = at("t-api").content.firstElementChild;
64         t_verb = at("t-verb").content.firstElementChild;
65         t_logmsg = at("t-logmsg").content.firstElementChild;
66         t_traceevent = at("t-traceevent").content.firstElementChild;
67         t_verbosity = at("t-verbosity").content.firstElementChild;
68         t_trace = at("t-trace").content.firstElementChild;
69
70         root_node = at("root");
71         connected_node = at("connected");
72         trace_events_node = at("trace-events");
73         logmsgs_node = at("logmsgs");
74         apis_node = at("apis");
75         all_node = at("all");
76
77         plug(t_api, ".verbosity", t_verbosity);
78         plug(t_api, ".trace", t_trace);
79         plug(all_node, ".trace", t_trace);
80         plug(all_node, ".verbosity", t_verbosity);
81         plug(at("common"), ".verbosity", t_verbosity);
82         for_all_nodes(root_node, ".opclo", function(n){n.onclick = on_toggle_opclo});
83         for_all_nodes(root_node, ".opclo ~ :not(.closedoff)", function(n){n.onclick = on_toggle_opclo});
84         for_all_nodes(root_node, ".verbosity select", function(n){n.onchange = set_verbosity});
85         for_all_nodes(root_node, ".trace-item input", function(n){n.onchange = on_trace_change});
86         at("disconnect").onclick = disconnect;
87         at("connect").onclick = on_connect;
88         at("droptracevts").onclick = drop_all_trace_events;
89         at("dropmsgs").onclick = drop_all_logmsgs;
90         at("stopmsgs").onclick = toggle_logmsgs;
91         start_logmsgs(false);
92         trace_events_node.onclick = on_toggle_traceevent;
93
94         connect();
95 }
96
97 function for_all_nodes(root, sel, fun) {
98         (root ? root : document).querySelectorAll(sel).forEach(fun);
99 }
100
101 function get(sel,x) {
102         if (!x)
103                 x = document;
104         var r = x.querySelector(sel);
105         return r;
106 }
107 function at(id) { return document.getElementById(id); }
108
109 function plug(target, sel, node) {
110         var x = get(sel, target);
111         var n = target.ownerDocument.importNode(node, true);
112         x.parentNode.insertBefore(n, x);
113         x.parentNode.removeChild(x);
114 }
115
116 function onopen() {
117         root_node.className = "on";
118         connected_node.innerHTML = "Connected " + ws.url;
119         connected_node.className = "ok";
120         ws.onevent("*", gotevent);
121         ws.onclose = onabort;
122         do_call("monitor/get", {apis:true,verbosity:true}, on_got_apis, on_error_apis);
123 }
124 function onabort() {
125         root_node.className = "off";
126         connected_node.innerHTML = "Connection Closed";
127         connected_node.className = "error";
128 }
129
130 function start_logmsgs(val) {
131         at("stopmsgs").textContent = (msgs = val) ? "Stop logs" : "Get logs";
132 }
133
134 function toggle_logmsgs() {
135         start_logmsgs(!msgs);
136 }
137
138 function drop_all_logmsgs() {
139         logmsgs_node.innerHTML = "";
140 }
141
142 function drop_all_trace_events() {
143         trace_events_node.innerHTML = "";
144 }
145
146 function add_logmsg(tag, content, add) {
147         if (!msgs) return;
148         var x = document.importNode(t_logmsg, true);
149         get(".tag", x).textContent = tag;
150         get(".content", x).textContent = content;
151         get(".close", x).onclick = function(evt){x.remove();};
152         if (add)
153                 x.className = x.className + " " + add;
154         logmsgs_node.prepend(x);
155 }
156
157 function add_error(tag, obj) {
158         add_logmsg(tag, JSON.stringify(obj, null, 1), "error");
159 }
160
161 function on_error_apis(obj) {
162         add_error("can't get apis", obj);
163 }
164
165 function do_call(api_verb, request, onsuccess, onerror) {
166         var call = api_verb + "(" + JSON.stringify(request, null, 1) + ")";
167         add_logmsg(call, "", "call");
168         ws.call(api_verb, request).then(
169                 function(obj){
170                         add_logmsg(call + " SUCCESS:", JSON.stringify(obj, null, 1), "retok");
171                         if (onsuccess)
172                                 onsuccess(obj);
173                 },
174                 function(obj){
175                         add_logmsg(call + " ERROR:", JSON.stringify(obj, null, 1), "reterr");
176                         if (onerror)
177                                 onerror(obj);
178                 });
179 }
180
181 /* show all verbosities */
182 function on_got_verbosities(obj) {
183         inhibit = true;
184         _.each(obj.response.verbosity, function(verbosity, api_name){
185                 if (api_name == "monitor") return;
186                 var node = api_name ? apis[api_name].node : at("common");
187                 if (node)
188                         get(".verbosity option[value='"+verbosity+"']", node).selected = true;
189         });
190         inhibit = false;
191 }
192
193 function set_verbosity(evt) {
194         if (inhibit) return;
195         inhibit = true;
196         var obj = evt.target;
197         var req = {verbosity:{}};
198         var name = obj.API ? obj.API.name : obj === get(".select", all_node) ? "*" : "";
199         if (name != "*") {
200                 req.verbosity[name] = obj.value;
201         } else {
202                 req.verbosity = obj.value;
203         }
204         inhibit = false;
205         do_call("monitor/set", req);
206         do_call("monitor/get", {verbosity:true}, on_got_verbosities);
207 }
208
209 /* show all apis */
210 function on_got_apis(obj) {
211         inhibit = true;
212         _.each(obj.response.apis, function(api_desc, api_name){
213                 if (api_name == "monitor") return;
214                 var api = apis[api_name];
215                 if (!api) {
216                         api = {
217                                 node: document.importNode(t_api, true),
218                                 verbs: {},
219                                 name: api_name
220                         };
221                         api.node.API = api;
222                         api.node.dataset.api = api_name;
223                         api.vnode = get(".verbs", api.node);
224                         apis[api_name] = api;
225                         get(".name", api.node).textContent = api_name;
226                         get(".desc", api.node).textContent = api_desc.info.description || "";
227                         for_all_nodes(api.node, ".opclo", function(n){n.onclick = on_toggle_opclo});
228                         for_all_nodes(api.node, ".opclo ~ :not(.closedoff)", function(n){n.onclick = on_toggle_opclo});
229                         for_all_nodes(api.node, ".trace-item input", function(n){n.onchange = on_trace_change});
230                         apis_node.append(api.node);
231                         _.each(api_desc.paths, function(verb_desc, path_name){
232                                 var verb_name = path_name.substring(1);
233                                 var verb = api.verbs[verb_name];
234                                 if (!verb) {
235                                         verb = {
236                                                 node: document.importNode(t_verb, true),
237                                                 name: verb_name,
238                                                 api: api
239                                         };
240                                         verb.node.VERB = verb;
241                                         verb.node.dataset.verb = verb_name;
242                                         api.verbs[verb_name] = verb;
243                                         get(".name", verb.node).textContent = verb_name;
244                                         var g = verb_desc.get ||{};
245                                         var r = g["responses"] || {};
246                                         var t = r["200"] || {};
247                                         var d = t.description || "";
248                                         get(".desc", verb.node).textContent = d;
249                                         if (show_perms) {
250                                                 var p = g["x-permissions"] || "";
251                                                 get(".perm", verb.node).textContent = p ? JSON.stringify(p, null, 1) : "";
252                                         }
253                                         api.vnode.append(verb.node);
254                                 }
255                         });
256                         var s = get(".verbosity select", api.node);
257                         s.API = api;
258                         s.onchange = set_verbosity;
259                 }
260         });
261         inhibit = false;
262         on_got_verbosities(obj);
263 }
264
265 function on_toggle_opclo(evt) {
266         toggle_opened_closed(evt.target.parentElement);
267 }
268
269 function on_trace_change(evt) {
270         var obj = evt.target;
271         var tra = obj.parentElement;
272         while (tra && !tra.dataset.trace)
273                 tra = tra.parentElement;
274         var api = tra;
275         while (api && !api.dataset.api)
276                 api = api.parentElement;
277         var tag = api.dataset.api + "/" + tra.dataset.trace;
278         if (tra) {
279                 var drop = false;
280                 for_all_nodes(tra, "input", function(n){
281                         if (n.checked) {
282                                 n.checked = false;
283                                 if (n != obj && n.value != "no")
284                                         drop = true;
285                         }
286                 });
287                 if (drop)
288                         do_call("monitor/trace", {drop: {tag: tag}});
289                 obj.checked = true;
290                 if (obj.value != "no") {
291                         var spec = {tag: tag, name: "trace"};
292                         spec[tra.dataset.trace] = obj.value;
293                         if (api.dataset.api != "*")
294                                 spec.api = api.dataset.api;
295                         do_call("monitor/trace", {add: spec});
296                 }
297         }
298 }
299
300 function makecontent(node, deep, val) {
301         if (--deep > 0) {
302                 if (_.isObject(val)) {
303                         node.append(makeobj(val, deep));
304                         return;
305                 }
306                 if (_.isArray(val)) {
307                         node.append(makearr(val, deep));
308                         return;
309                 }
310         }
311         node.innerHTML = obj2html(val);
312 }
313
314 function makearritem(tbl, deep, val) {
315         var tr = document.createElement("tr");
316         var td = document.createElement("td");
317         tr.append(td);
318         tbl.append(tr);
319         makecontent(td, deep, val);
320 }
321
322 function makearr(arr, deep) {
323         var node = document.createElement("table");
324         node.className = "array";
325         _.each(arr, function(v) { makearritem(node, deep, v);});
326         return node;
327 }
328
329 function makeobjitem(tbl, deep, key, val) {
330         var tr = document.createElement("tr");
331         var td1 = document.createElement("td");
332         var td2 = document.createElement("td");
333         tr.className = key;
334         tr.append(td1);
335         td1.textContent = key;
336         tr.append(td2);
337         tbl.append(tr);
338         makecontent(td2, deep, val);
339 }
340
341 function makeobj(obj, deep, ekey, eobj) {
342         var node = document.createElement("table");
343         node.className = "object";
344         _.each(_.keys(obj).sort(), function(k) { makeobjitem(node, deep, k, obj[k]);});
345         if (ekey)
346                 makeobjitem(node, deep, ekey, eobj);
347         return node;
348 }
349
350 function gotevent(obj) {
351         if (obj.event != "monitor/trace")
352                 add_logmsg("unexpected event!", JSON.stringify(obj, null, 1), "event");
353         else {
354                 add_logmsg("trace event", JSON.stringify(obj, null, 1), "trace");
355                 gottraceevent(obj);
356         }
357 }
358
359 function gottraceevent(obj) {
360         var data = obj.data;
361         var type = _.find(["request", "service", "daemon", "event"],function(x){return x in data;});
362         var desc = data[type];
363         if (!show_monitor_events) {
364                 if (type == "event" ? desc.name.startsWith("monitor/") : desc.api == "monitor")
365                         return;
366         }
367         var x = document.importNode(t_traceevent, true);
368         x.dataset.event = obj;
369         get(".close", x).onclick = function(evt){x.remove();};
370         x.className = x.className + " " + type;
371         get(".time", x).textContent = data.time;
372         get(".tag", x).textContent = ({
373                 request: function(r) { return r.api + "/" + r.verb + "  [" + r.index + "] " + r.action; },
374                 service: function(r) { return r.api + "@" + r.action; },
375                 daemon: function(r) { return r.api + ":" + r.action; },
376                 event: function(r) { return r.name + "!" + r.action; },
377                 })[type](desc);
378         var tab = makeobj(desc, 4);
379         if ("data" in data)
380                 makeobjitem(tab, 1, "data", data.data);
381         get(".content", x).append(tab);
382         trace_events_node.append(x);
383 }
384
385 function toggle_opened_closed(node, defval) {
386         var matched = false;
387         var cs = node.className.split(" ").map(
388                 function(x){
389                         if (!matched) {
390                                 switch(x) {
391                                 case "closed": matched = true; return "opened";
392                                 case "opened": matched = true; return "closed";
393                                 }
394                         }
395                         return x;
396                 }).join(" ");
397         if (!matched)
398                 cs = cs + " " + (defval || "closed");
399         node.className = cs;
400 }
401
402 function on_toggle_traceevent(evt) {
403         if (getSelection() != "") return;
404         var node = evt.target;
405         while(node && node.parentElement != trace_events_node)
406                 node = node.parentElement;
407         node && toggle_opened_closed(node);
408 }
409
410 function obj2html(json) {
411         json = JSON.stringify(json, undefined, 2);
412         json = json.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
413         return json.replace(
414                 /("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g,
415                 function (match) {
416                         var cls = 'number';
417                         if (/^"/.test(match)) {
418                                 if (/:$/.test(match)) {
419                                         cls = 'key';
420                                 } else {
421                                         cls = 'string';
422                                 }
423                         } else if (/true|false/.test(match)) {
424                                 cls = 'boolean';
425                         } else if (/null/.test(match)) {
426                                 cls = 'null';
427                         }
428                         return '<span class="json ' + cls + '">' + match + '</span>';
429                 });
430 }
431