227b1a97b13a4ef5c1763cc79801880143ea7af4
[src/app-framework-binder.git] / test / monitoring / AFB.js
1 /*
2  * Copyright (C) 2017-2019 "IoT.bzh"
3  * Author: José Bollo <jose.bollo@iot.bzh>
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *   http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 AFB = function(base, initialtoken){
18
19 if (typeof base != "object")
20         base = { base: base, token: initialtoken };
21
22 var initial = {
23         base: base.base || "api",
24         token: initialtoken || base.token 
25                 || URLSearchParams(window.location.search).get('access_token')
26                 || URLSearchParams(window.location.search).get('token')
27                 || "HELLO",
28         host: base.host || window.location.host,
29         url: base.url || undefined
30 };
31
32 var urlws = initial.url || "ws://"+initial.host+"/"+initial.base;
33
34 /*********************************************/
35 /****                                     ****/
36 /****             AFB_context             ****/
37 /****                                     ****/
38 /*********************************************/
39 var AFB_context;
40 {
41         var UUID = undefined;
42         var TOKEN = initial.token;
43
44         var context = function(token, uuid) {
45                 this.token = token;
46                 this.uuid = uuid;
47         }
48
49         context.prototype = {
50                 get token() {return TOKEN;},
51                 set token(tok) {if(tok) TOKEN=tok;},
52                 get uuid() {return UUID;},
53                 set uuid(id) {if(id) UUID=id;}
54         };
55
56         AFB_context = new context();
57 }
58 /*********************************************/
59 /****                                     ****/
60 /****             AFB_websocket           ****/
61 /****                                     ****/
62 /*********************************************/
63 var AFB_websocket;
64 {
65         var CALL = 2;
66         var RETOK = 3;
67         var RETERR = 4;
68         var EVENT = 5;
69
70         var PROTO1 = "x-afb-ws-json1";
71
72         AFB_websocket = function(on_open, on_abort) {
73                 var u = urlws, p = '?';
74                 if (AFB_context.token) {
75                         u = u + '?x-afb-token=' + AFB_context.token;
76                         p = '&';
77                 }
78                 if (AFB_context.uuid)
79                         u = u + p + 'x-afb-uuid=' + AFB_context.uuid;
80                 this.ws = new WebSocket(u, [ PROTO1 ]);
81                 this.url = u;
82                 this.pendings = {};
83                 this.awaitens = {};
84                 this.counter = 0;
85                 this.ws.onopen = onopen.bind(this);
86                 this.ws.onerror = onerror.bind(this);
87                 this.ws.onclose = onclose.bind(this);
88                 this.ws.onmessage = onmessage.bind(this);
89                 this.onopen = on_open;
90                 this.onabort = on_abort;
91         }
92
93         function onerror(event) {
94                 var f = this.onabort;
95                 if (f) {
96                         delete this.onopen;
97                         delete this.onabort;
98                         f(this);
99                 }
100                 this.onerror && this.onerror(this);
101         }
102
103         function onopen(event) {
104                 var f = this.onopen;
105                 delete this.onopen;
106                 delete this.onabort;
107                 f && f(this);
108         }
109
110         function onclose(event) {
111                 var err = {
112                         jtype: 'afb-reply',
113                         request: {
114                                 status: 'disconnected',
115                                 info: 'server hung up'
116                         }
117                 };
118                 for (var id in this.pendings) {
119                         try { this.pendings[id][1](err); } catch (x) {/*NOTHING*/}
120                 }
121                 this.pendings = {};
122                 this.onclose && this.onclose();
123         }
124
125         function fire(awaitens, name, data) {
126                 var a = awaitens[name];
127                 if (a)
128                         a.forEach(function(handler){handler(data, name);});
129                 var i = name.indexOf("/");
130                 if (i >= 0) {
131                         a = awaitens[name.substring(0,i)];
132                         if (a)
133                                 a.forEach(function(handler){handler(data, name);});
134                 }
135                 a = awaitens["*"];
136                 if (a)
137                         a.forEach(function(handler){handler(data, name);});
138         }
139
140         function reply(pendings, id, ans, offset) {
141                 if (id in pendings) {
142                         var p = pendings[id];
143                         delete pendings[id];
144                         try { p[offset](ans); } catch (x) {/*TODO?*/}
145                 }
146         }
147
148         function onmessage(event) {
149                 var obj = JSON.parse(event.data);
150                 var code = obj[0];
151                 var id = obj[1];
152                 var ans = obj[2];
153                 AFB_context.token = obj[3];
154                 switch (code) {
155                 case RETOK:
156                         reply(this.pendings, id, ans, 0);
157                         break;
158                 case RETERR:
159                         reply(this.pendings, id, ans, 1);
160                         break;
161                 case EVENT:
162                 default:
163                         fire(this.awaitens, id, ans);
164                         break;
165                 }
166         }
167
168         function close() {
169                 this.ws.close();
170                 this.ws.onopen = 
171                 this.ws.onerror = 
172                 this.ws.onclose = 
173                 this.ws.onmessage = 
174                 this.onopen = 
175                 this.onabort = function(){};
176         }
177
178         function call(method, request, callid) {
179                 return new Promise((function(resolve, reject){
180                         var id, arr;
181                         if (callid) {
182                                 id = String(callid);
183                                 if (id in this.pendings)
184                                         throw new Error("pending callid("+id+") exists");
185                         } else {
186                                 do {
187                                         id = String(this.counter = 4095 & (this.counter + 1));
188                                 } while (id in this.pendings);
189                         }
190                         this.pendings[id] = [ resolve, reject ];
191                         arr = [CALL, id, method, request ];
192                         if (AFB_context.token) arr.push(AFB_context.token);
193                         this.ws.send(JSON.stringify(arr));
194                 }).bind(this));
195         }
196
197         function onevent(name, handler) {
198                 var id = name;
199                 var list = this.awaitens[id] || (this.awaitens[id] = []);
200                 list.push(handler);
201         }
202
203         AFB_websocket.prototype = {
204                 close: close,
205                 call: call,
206                 onevent: onevent
207         };
208 }
209 /*********************************************/
210 /****                                     ****/
211 /****                                     ****/
212 /****                                     ****/
213 /*********************************************/
214 return {
215         context: AFB_context,
216         ws: AFB_websocket
217 };
218 };
219