luci-app-olsr: handle empty result for non-status tables
[oweals/luci.git] / modules / luci-base / htdocs / luci-static / resources / rpc.js
1 'use strict';
2
3 var rpcRequestID = 1,
4     rpcSessionID = L.env.sessionid || '00000000000000000000000000000000',
5     rpcBaseURL = L.url('admin/ubus');
6
7 return L.Class.extend({
8         call: function(req, cb) {
9                 var q = '';
10
11                 if (Array.isArray(req)) {
12                         if (req.length == 0)
13                                 return Promise.resolve([]);
14
15                         for (var i = 0; i < req.length; i++)
16                                 q += '%s%s.%s'.format(
17                                         q ? ';' : '/',
18                                         req[i].params[1],
19                                         req[i].params[2]
20                                 );
21                 }
22                 else {
23                         q += '/%s.%s'.format(req.params[1], req.params[2]);
24                 }
25
26                 return L.Request.post(rpcBaseURL + q, req, {
27                         timeout: (L.env.rpctimeout || 5) * 1000,
28                         credentials: true
29                 }).then(cb);
30         },
31
32         handleListReply: function(req, msg) {
33                 var list = msg.result;
34
35                 /* verify message frame */
36                 if (typeof(msg) != 'object' || msg.jsonrpc != '2.0' || !msg.id || !Array.isArray(list))
37                         list = [ ];
38
39                 req.resolve(list);
40         },
41
42         handleCallReply: function(req, res) {
43                 var type = Object.prototype.toString,
44                     msg = null;
45
46                 if (!res.ok)
47                         L.error('RPCError', 'RPC call failed with HTTP error %d: %s',
48                                 res.status, res.statusText || '?');
49
50                 msg = res.json();
51
52                 /* fetch response attribute and verify returned type */
53                 var ret = undefined;
54
55                 /* verify message frame */
56                 if (typeof(msg) == 'object' && msg.jsonrpc == '2.0') {
57                         if (typeof(msg.error) == 'object' && msg.error.code && msg.error.message)
58                                 req.reject(new Error('RPC call failed with error %d: %s'
59                                         .format(msg.error.code, msg.error.message || '?')));
60                         else if (Array.isArray(msg.result) && msg.result[0] == 0)
61                                 ret = (msg.result.length > 1) ? msg.result[1] : msg.result[0];
62                 }
63                 else {
64                         req.reject(new Error('Invalid message frame received'));
65                 }
66
67                 if (req.expect) {
68                         for (var key in req.expect) {
69                                 if (ret != null && key != '')
70                                         ret = ret[key];
71
72                                 if (ret == null || type.call(ret) != type.call(req.expect[key]))
73                                         ret = req.expect[key];
74
75                                 break;
76                         }
77                 }
78
79                 /* apply filter */
80                 if (typeof(req.filter) == 'function') {
81                         req.priv[0] = ret;
82                         req.priv[1] = req.params;
83                         ret = req.filter.apply(this, req.priv);
84                 }
85
86                 req.resolve(ret);
87         },
88
89         list: function() {
90                 var msg = {
91                         jsonrpc: '2.0',
92                         id:      rpcRequestID++,
93                         method:  'list',
94                         params:  arguments.length ? this.varargs(arguments) : undefined
95                 };
96
97                 return this.call(msg, this.handleListReply);
98         },
99
100         declare: function(options) {
101                 return Function.prototype.bind.call(function(rpc, options) {
102                         var args = this.varargs(arguments, 2);
103                         return new Promise(function(resolveFn, rejectFn) {
104                                 /* build parameter object */
105                                 var p_off = 0;
106                                 var params = { };
107                                 if (Array.isArray(options.params))
108                                         for (p_off = 0; p_off < options.params.length; p_off++)
109                                                 params[options.params[p_off]] = args[p_off];
110
111                                 /* all remaining arguments are private args */
112                                 var priv = [ undefined, undefined ];
113                                 for (; p_off < args.length; p_off++)
114                                         priv.push(args[p_off]);
115
116                                 /* store request info */
117                                 var req = {
118                                         expect:  options.expect,
119                                         filter:  options.filter,
120                                         resolve: resolveFn,
121                                         reject:  rejectFn,
122                                         params:  params,
123                                         priv:    priv
124                                 };
125
126                                 /* build message object */
127                                 var msg = {
128                                         jsonrpc: '2.0',
129                                         id:      rpcRequestID++,
130                                         method:  'call',
131                                         params:  [
132                                                 rpcSessionID,
133                                                 options.object,
134                                                 options.method,
135                                                 params
136                                         ]
137                                 };
138
139                                 /* call rpc */
140                                 rpc.call(msg, rpc.handleCallReply.bind(rpc, req));
141                         });
142                 }, this, this, options);
143         },
144
145         getSessionID: function() {
146                 return rpcSessionID;
147         },
148
149         setSessionID: function(sid) {
150                 rpcSessionID = sid;
151         },
152
153         getBaseURL: function() {
154                 return rpcBaseURL;
155         },
156
157         setBaseURL: function(url) {
158                 rpcBaseURL = url;
159         }
160 });