-
Notifications
You must be signed in to change notification settings - Fork 2
/
specification.html
440 lines (402 loc) · 25.2 KB
/
specification.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
---
layout: page
title: Specifications
permalink: /specifications/
icon: fa-file-text-o
classes: specifications col-md-10 col-md-offset-1 col-sm-10 col-sm-offset-1
stylesheet: /css/pages/specifications.css
---
<h1>Woopsa Protocol Specifications</h1>
<h2>Changelogs</h2>
<div class="table-responsive">
<table class="table table-striped table-bordered table-sm">
<thead>
<tr>
<th>Date</th>
<th>Version</th>
<th>Info</th>
</tr>
</thead>
<tbody>
<tr>
<td>12.08.2016</td>
<td>1.2.0</td>
<td>Initial Version</td>
</tr>
<tr>
<td>05.07.2022</td>
<td>1.2.1</td>
<td>Precision of the value returned from the verb write</td>
</tr>
</tbody>
</table>
</div>
<h2>Table of contents</h2>
<div id="table-of-contents">
<ul>
<li><a href="#introduction">Introduction</a></li>
<li><a href="#definitions">Definitions</a></li>
<li>
<a href="#the-woopsa-interface">The Woopsa interface</a>
<ul>
<li><a href="#the-object-model">The object model</a></li>
<li><a href="#the-woopsa-value-types">The Woopsa value types</a></li>
<li><a href="#woopsa-values">Woopsa values</a></li>
</ul>
</li>
<li>
<a href="#communications-protocol">Communications protocol</a>
<ul>
<li><a href="#transport">Transport</a></li>
<li><a href="#serialization">Serialization</a></li>
</ul>
</li>
<li>
<a href="#verbs">Verbs</a>
<ul>
<li><a href="#meta">Meta</a></li>
<li><a href="#read">Read</a></li>
<li><a href="#write">Write</a></li>
<li><a href="#invoke">Invoke</a></li>
</ul>
</li>
<li>
<a href="#error-handling">Error handling</a>
<ul>
<li><a href="#error-transport">Error transport</a></li>
<li><a href="#error-types">Error types</a></li>
</ul>
</li>
<li>
<a href="#native-extensions">Native extensions</a>
<ul>
<li><a href="#publish-subscribe-with-the-subscriptionservice-woopsa-object">Publish/Subscribe with the <samp>SubscriptionService</samp> Woopsa object</a></li>
<li><a href="#multiple-requests-with-the-multirequest-woopsa-method">Multiple requests with the <samp>MultiRequest</samp> Woopsa method</a></li>
</ul>
</li>
</ul>
</div>
<h2 id="introduction">Introduction</h2>
<p>Woopsa stands for <strong>Web Object-Oriented Protocol for Software and Automation</strong>.</p>
<p>It was designed with simplicity and interoperability in mind. The object-oriented part of the name means that data in Woopsa is hierarchical.</p>
<h2 id="definitions">Definitions</h2>
<dl class="dl-standard">
<dt>Server</dt>
<dd>A Woopsa Server accepts connections from clients and <strong>publishes</strong> data, making it available through the Woopsa Interface.</dd>
<dt>Client</dt>
<dd>A Woopsa Client connects to a Woopsa Server and <strong>reads</strong> and <strong>writes</strong> data, <strong>invokes functions</strong> and <strong>explores the metadata</strong> using the Woopsa Interface</dd>
<dt>Metadata</dt>
<dd>Metadata is a piece of data that represents the hierarchy of an object. It does not contain actual data.</dd>
</dl>
<h2 id="the-woopsa-interface">The Woopsa Interface</h2>
<p>Because data in Woopsa is hierarchical, an interface must be specified which describes how data is structured and the kind of data that's available. This is called the Woopsa interface and it is described below. This section does <strong>not</strong> describe how data is serialized or transported.</p>
<h3 id="the-object-model">The Object Model</h3>
<p><img src="{{ site.baseUrl }}/img/woopsa-full-uml.png" class="img-responsive" alt="Full Woopsa interface UML diagram"></p>
<p>Data exchanged between Woopsa servers and clients must conform to the above object model. Everything in Woopsa is a <samp>IWoopsaElement</samp>, which means that everything in woopsa has a <samp>Name</samp> and thus a <strong>path</strong>. However, <samp>IWoopsaElement</samp> and <samp>IWoopsaContainer</samp> used only for hierarchy's sake and only <samp>IWoopsaObject</samp>s are ever instanciated.</p>
<h3 id="the-woopsa-value-types">The Woopsa Value Types</h3>
<p>There is a total of 10 different types of data that can be used in Woopsa. Their serialization is discussed in the <strong>Serialization</strong> section below.</p>
<dl class="dl-standard">
<dt>Null</dt>
<dd>Used to represent absence data — for example, this is the <samp>ReturnType</samp> for <samp>void</samp> methods</dd>
<dt>Logical</dt>
<dd>Boolean true/false value</dd>
<dt>Integer</dt>
<dd>64-bit integer value</dd>
<dt>Real</dt>
<dd>Real number, floating-point representation</dd>
<dt>DateTime</dt>
<dd>A moment in time</dt>
<dt>TimeSpan</dt>
<dd>A duration of time</dd>
<dt>Text</dt>
<dd>A chain of characters representing a string.</dd>
<dt>WoopsaLink</dt>
<dd>Link to another <samp>WoopsaElement</samp> in the object tree</dd>
<dt>JsonData</dt>
<dd>Any data represented in the JSON format</dd>
<dt>ResourceUrl</dt>
<dd>A URL to a resource external to the Woopsa server</dd>
</dl>
<h3 id="woopsa-values">Woopsa Values</h3>
<p>Woopsa values can be represented internally in many different ways. The implementation is not restricted to any particular method of storing the data. However, every <samp>IWoopsaValue</samp> needs an <samp>AsText</samp> representation. This representation should not change if the internal value hasn't changed.</p>
<p>Common to every <samp>IWoopsaValue</samp> is also its <samp>Type</samp> and an optional <samp>TimeStamp</samp> which is meant to represent the date/time at which this value became effective.</p>
<h2 id="communications-protocol">Communications protocol</h2>
<h3 id="transport">Transport</h3>
<p>Communication between a client and a server is always initiated by a client using the <strong>HTTP 1.1</strong> protocol. Only the <samp>GET</samp> and <samp>POST</samp> methods are used. Woopsa servers are available on a root path (URI) called the <strong>route prefix</strong>.</p>
<p>All requests by the client are thus made on URLs of the following form:</p>
<p><samp>http://{server-address}/{route-prefix}/{woopsa-verb}/{woopsa-path}</samp></p>
<p>When the client has no data to send to the server (<samp>read</samp> and <samp>meta</samp>), requests are made using <samp>GET</samp> and the URL is the only piece of information needed by the server.</p>
<p>When the client has data to send to the server (<samp>write</samp> and <samp>invoke</samp>), requests are made using <samp>POST</samp> and the HTTP <samp>Content-Type</samp> must be <samp>application/x-www-form-urlencoded</samp>. Form data must be represented the way it would be if it were JSON-serialized. This means, for example, that numbers need to use a "dot" (.) as the decimal separator, and must not have any thousand separators.</p>
<p>The Woopsa path is represented as a standard HTTP path URI. The path separator is thus also the <samp>/</samp> character.</p>
<h3 id="serialization">Serialization</h3>
<p>The serialization method chosen for Woopsa is <strong><a href="http://www.json.org">JSON</a></strong>.</p>
<p>Only server responses are encoded as JSON. Requests from clients are described above, and use standard HTTP methods for sending data.</p>
<p>The following list details Woopsa types and their JSON representation:</p>
<dl class="dl-standard">
<dt>Null</dt>
<dd>JSON <samp>null</samp>.</dd>
<dt>Integer, Real</dt>
<dd>JSON <samp>number</samp>.</dd>
<dt>DateTime</dt>
<dd>JSON <samp>string</samp>, representing the date in <a href="http://www.ecma-international.org/ecma-262/5.1/#sec-15.9.1.15" class="example" title="Example: "2015-10-14T15:06:46.5992819Z"">ISO-8601 as described in section 15.9.1.15</a>.</dd>
<dt>TimeSpan</dt>
<dd>JSON <samp>number</samp>, representing a time duration in seconds (can be fractional).</dd>
<dt>Text</dt>
<dd>JSON <samp>string</samp>.</dd>
<dt>WoopsaLink</dt>
<dd>
JSON <samp>string</samp> represented in the following manner: <samp>{server-url}#{woopsa-path}</samp>, where
<dl class="dl-standard">
<dt>{server-url}</dt>
<dd>The complete URL of the server, including the route prefix. Can be omitted if the element resides on the same server, in which case the <samp>#</samp> sign must also be omitted.</dd>
<dt>{woopsa-path}</dt>
<dd>The complete path of the Woopsa element</dd>
</dl>
</dd>
<dt>JsonData</dt>
<dd>JSON <samp>object</samp>, <samp>array</samp> or any other valid JSON construct.</dd>
<dt>ResourceUrl</dt>
<dd>JSON <samp>string</samp> representing any URL (http://, ftp://, etc...)</dd>
</dl>
<h2 id="verbs">Verbs</h2>
<p>Verbs are a way in Woopsa for the client to tell the server what to reply. There are only 4 verbs in Woopsa.</p>
<h3 id="meta">Meta</h3>
<p>The <samp>meta</samp> verb allows the client to receive the entire structure of a Woopsa object specified by <samp>{path}</samp>.</p>
<p>To get the root object, the path must be blank. <samp>meta</samp> is not recursive: if a client needs to get the entire hierarchy of the object, it must make requests for every inner object.</p>
<dl class="dl-horizontal">
<dt>HTTP Method:</dt>
<dd>GET</dd>
<dt>Usage:</dt>
<dd><samp>/meta/{path}</samp></dd>
<dt>Return value:</dt>
<dd>
{% highlight json %}
{
"Name": string,
"Items": [string],
"Properties": [{
"Name": string,
"Type": string,
"ReadOnly": boolean
}],
"Methods": [{
"Name": string,
"ReturnType": string,
"ArgumentInfos": [{
"Name": string,
"Type": string
}]
}]
}{% endhighlight %}
</dd>
</dl>
<h3 id="read">Read</h3>
<p>The <samp>read</samp> verb allows the client to get the value of a Woopsa property specified by <samp>{path}</samp>.</p>
<p>The <samp>Value</samp> field returned by the server can be any valid JSON construct, usually <samp>string</samp> or <samp>number</samp>, but can also be an entire JSON <samp>object</samp> when <samp>Type</samp> is <samp>JsonData</samp></p>
<p>The <samp>TimeStamp</samp> field returned by the server is optional.</p>
<dl class="dl-horizontal">
<dt>HTTP Method:</dt>
<dd>GET</dd>
<dt>Usage:</dt>
<dd><samp>/read/{path}</samp></dd>
<dt>Return value:</dt>
<dd><p>A JSON-serialized WoopsaValue: </p>
{% highlight json %}
{
"Value": number/string/array/object,
"Type": string,
"TimeStamp": string
}{% endhighlight %}
</dd>
</dl>
<h3 id="write">Write</h3>
<p>The <samp>write</samp> verb allows the client to write a value to a Woopsa property specified by <samp>{path}</samp>.</p>
<p>The server replies with the value that was written.</p>
<dl class="dl-horizontal">
<dt>HTTP Method:</dt>
<dd>POST</dd>
<dt>Usage:</dt>
<dd><samp>/write/{path}</samp></dd>
<dt>HTTP POST Variables:</dt>
<dd>
<ul class="list-unstyled">
<li><strong>value</strong>: The value to write to the property. Must be a <samp>string</samp> or <samp>number</samp>. Writing to JsonData is not enforced in any way and its implementation is left to those who might need it.</li>
</ul>
</dd>
<dt>Return value:</dt>
<dd>The last applied value as JSON serialized WoopsaValue, <em>see <samp>read</samp></em></dd>
</dl>
<h3 id="invoke">Invoke</h3>
<p>The <samp>invoke</samp> verb allows the client to invoke (call) a Woopsa method specified by <samp>{path}</samp>.</p>
<p>The server replies with the return value of the function if the function is non-void. In the case of a void function, no data is returned by the server.</p>
<dl class="dl-horizontal">
<dt>HTTP Method:</dt>
<dd>POST</dd>
<dt>Usage:</dt>
<dd><samp>/invoke/{path}</samp></dd>
<dt>HTTP POST Variables:</dt>
<dd>
A key/value pair of arguments for the method.
</dd>
<dt>Return value:</dt>
<dd>A JSON-serialized WoopsaValue, <em>see <samp>read</samp></em></dd>
</dl>
<h2 id="error-handling">Error handling</h2>
<h3 id="error-transport">Error transport</h3>
<p>Some situations can cause errors in Woopsa, for example when trying to write to a read-only property or when an invoked method throws an exception. Errors can happen on any verb, so client libraries must handle these possible situations accordingly.</p>
<p>In the case of an error, the HTTP status sent as a response <strong>must not</strong> be of <samp>HTTP 200 OK</samp>. Instead, the status code should be one of the <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html" class="example">standard HTTP error codes defined in RFC 2616, section 10</a>. The HTTP status text should contain a detailed description of the problem. This allows the client to quickly know there was an error by looking only at the headers.</p>
<p>The content of the response from the server is a JSON representation of the error, including the error type:</p>
{% highlight json %}
{
"Error": true,
"Message": string,
"Type": string
}{% endhighlight %}
<h3 id="error-types">Error types</h3>
<p>The user is free to create new error types. However, the following types are defined in Woopsa and should not be used for other purposes:</p>
<dl>
<dt>WoopsaException</dt>
<dd>A generic type for errors that happen inside the Woopsa server.</dd>
<dt>WoopsaNotFoundException</dt>
<dd>This error is thrown when the client has tried to access an non-existant property, method or item. HTTP status code will be set to <samp>404 Not Found</samp>.</dd>
<dt>WoopsaInvalidOperationException</dt>
<dd>This error is thrown when the client has made an invalid request to the server, such as trying to invoke a method with the wrong parameters or trying to write to a read-only property. HTTP status code will be set to <samp>400 Bad Request</samp>.</dd>
<dt>WoopsaNotificationsLostException</dt>
<dd>This error applies only to the <samp>SubscriptionService</samp>. Thrown when the inner queue of notifications on the service has been filled up completely and old notifications must be erased to make space for new notifications.</dd>
<dt>WoopsaInvalidSubscriptionChannelException</dt>
<dd>This error applies only to the <samp>SubscriptionService</samp>. Thrown when the client makes a reference to an unexisting or invalid subscription channel. This usually means that the server has been reset between two <samp>WaitNotification</samp> calls, and that the client must re-create the <samp>SubscriptionChannel</samp></dd>
</dl>
<h2 id="native-extensions">Native extensions</h2>
<p>Extensions in Woopsa allow to extend the basic functions of the protocol. They are found on the root object and can be methods or entire objects.</p>
<h3 id="publish-subscribe-with-the-subscriptionservice-woopsa-object">Publish/Subscribe with the <samp>SubscriptionService</samp> Woopsa object</h3>
<p>The <samp>SubscriptionService</samp> is a <samp>WoopsaObject</samp> that lies at the root of a Woopsa server when it supports notifications. It contains 4 Woopsa methods that allow the client to create a Subscription Channel, add/remove Subscriptions, and receive notifications. If a server does not implement notifications, it should simply not offer this object at the root.</p>
<p>When a <em>Subscription Channel</em> is created, the server creates an internal <em>Notification Queue</em> for that <em>Subscription Channel</em>. The client then registers <em>Subscriptions</em> by specifying which properties it would like to receive notifications for. The server then starts polling the value of the registered properties at the specified <em>Monitor Interval</em> and creates a <em>Notification Queue</em> for each <em>Subscription</em>. When the value of the property changes, a <em>Notification</em> is put into the <em>Notification Queue</em>. These <em>Notifications</em> are then removed from that queue and put into the <em>Subscription Channel</em>'s queue at a rate of <em>Publish Interval</em>.</p>
<p>The client can then call <samp>WaitNotification</samp> on the server. This method is blocking and has a default timeout of 5 seconds. As soon as one or more <em>Notifications</em> are available in the <em>Notification Queue</em> of the <em>Subscription Channel</em>, the function returns them.</p>
<p><a href="{{ '/img/subscription-service.png' | prepend:site.baseurl }}"><img src="{{ '/img/subscription-service.png' | prepend:site.baseurl }}" class="subscription-service-image"/></a></p>
<h4><samp>CreateSubscriptionChannel(Integer NotificationQueueSize) : Integer</samp></h4>
<p>Creates a new <em>Subscription Channel</em> on the server. A <em>Subscription Channel</em> regroups all subscriptions created in it. </p>
<dl class="dl-horizontal">
<dt>Arguments:</dt>
<dd>
<ul class="list-unstyled">
<li><strong>NotificationQueueSize</strong>: <samp>Integer</samp>, the maximum amount of notifications a server can store before it is forced to send notifications to the client.</li>
</ul>
</dd>
<dt>Return value:</dt>
<dd>
<samp>Integer</samp>, a unique numerical identifier for this <em>Subscription Channel</em>, to be passed as an argument to <samp>RegisterSubscription</samp>, <samp>UnregisterSubscription</samp> and <samp>WaitNotification</samp>.
</dd>
</dl>
<p><strong>Note:</strong> If a <em>Subscription Channel</em> is unused for more than 20 minutes, the server deletes it and all the subscriptions are removed from the server as well.</p>
<h4><samp>RegisterSubscription(Integer SubscriptionChannel, WoopsaLink PropertyLink, TimeSpan MonitorInterval, TimeSpan PublishInterval) : Integer</samp></h4>
<p>Registers a <em>Woopsa Property</em> specified by <samp>PropertyLink</samp> to be monitored by the provided <samp>SubscriptionChannel</samp>. Once a property is registered, changes to that property will add a notification to the channel's queue and thus push notifications to the client.</p>
<dl class="dl-horizontal">
<dt>Arguments:</dt>
<dd>
<ul class="list-unstyled">
<li><strong>SubscriptionChannel</strong>: <samp>Integer</samp>, the unique numerical identifier of the <em>Subscription Channel</em> returned by a previous <samp>CreateSubscriptionChannel</samp> call.</li>
<li><strong>PropertyLink</strong>: <samp>WoopsaLink</samp>, the path of the <em>Woopsa Property</em> to monitor.</li>
<li><strong>MonitorInterval</strong>: <samp>TimeSpan</samp>, the rate at which the server should poll this variable to check for changes. Recommended: 0.1 seconds.
When MonitorInterval is defined to 0, property is polled at the rate of PublishInterval, and only the latest value change is returned to WaitNotification.
</li>
<li><strong>PublishInterval</strong>: <samp>TimeSpan</samp>, the maximum rate at which notifications can be published to the queue. Recommended: 0.1 seconds.
When MonitorInterval and PublishInterval are both defined to 0, there will be only 1 notification at the time of the subscription, and the property will not be polled.
</li>
</ul>
</dd>
<dt>Return value:</dt>
<dd>
<samp>Integer</samp>, a numerical identifier for this <em>Subscription</em>, to be passed as an argument to <samp>UnregisterSubscription</samp>.
</dd>
</dl>
<p>
<h4><samp>UnregisterSubscription(Integer SubscriptionChannel, Integer SubscriptionId) : Logical</samp></h4>
<p>Unregisters a <em>Woopsa Property</em> specified by the passed <samp>SubscriptionId</samp>. This will stop notifications for that property to be pushed to the client.</p>
<dl class="dl-horizontal">
<dt>Arguments:</dt>
<dd>
<ul class="list-unstyled">
<li><strong>SubscriptionChannel</strong>: <samp>Integer</samp>, the unique numerical identifier of the <em>Subscription Channel</em> returned by a previous <samp>CreateSubscriptionChannel</samp> call.</li>
<li><strong>SubscriptionId</strong>: <samp>Integer</samp>, the id of the <em>Subscription</em> returned by a previous <samp>RegisterSubscription</samp> call.</li>
</ul>
</dd>
<dt>Return value:</dt>
<dd>
<samp>Logical</samp>, <samp>true</samp> if the <em>Subscription</em> was unregistered successfully.
</dd>
</dl>
<h4><samp>WaitNotification(Integer SubscriptionChannel, Integer LastNotificationId) : JsonData</samp></h4>
<p>This method blocks until a notification is sent by the server. In case no <em>Notifications</em> have appeared in the queue, this function returns an empty value after <strong>5 seconds</strong>. This effectively creates a heartbeat and allows connection problems to be detected in 5 seconds or less.</p>
<p>In order to guarantee that no notifications are lost between a client and a server, each notification sent by the server is identified with a unique incremental number (<samp>Id</samp>). Upon calling this method, the client must tell the server which <samp>Id</samp> it was last able to process. Only then will the notifications be removed from the queue on the server.</p>
<p>If the client does not fetch notifications quickly enough, the notifications queue can fill up completely. In that case, the server will delete old notifications and this method will throw a <samp>WoopsaNotificationsLostException</samp>. The client must acknowledge this error before it can receive new notifications by calling this method with a <samp>LastNotificationId</samp> value of <samp>0</samp>. The first call to this method by a client should also have the <samp>LastNotificationId</samp> value of <samp>0</samp>.</p>
<p>Notification IDs are from 1 to 1'000'000'000 (one billion).</p>
<dl class="dl-horizontal">
<dt>Arguments:</dt>
<dd>
<ul class="list-unstyled">
<li><strong>SubscriptionChannel</strong>: <samp>Integer</samp>, the unique numerical identifier of the <em>Subscription Channel</em> returned by a previous <samp>CreateSubscriptionChannel</samp> call.</li>
<li><strong>LastNotificationId</strong>: <samp>Integer</samp>, the unique numerical identifier of the last <em>Notification</em> that the client was able to handle. If this number is a valid reference to a <em>Notification</em> in the current queue, the server will delete from this queue all <em>Notifications</em> that have an <samp>Id</samp> <em>less than or equal to</em> this Id.</li>
</ul>
</dd>
<dt>Return value:</dt>
<dd>
<samp>JsonData</samp> of type <samp>Array</samp>, the complete list of notifications in the server queue.
</dd>
</dl>
<p><strong>Note:</strong> Notifications are sent in the following format:</p>
{% highlight javascript %}
{
"Value": [
{
"Value": /* A JSON-serialized WoopsaValue, see _read_ */,
"SubscriptionId": /* The identifier of the subscription for which this notification is raised */,
"Id": number /* A unique, incremental identifier for this notification. */
},
..
],
"Type": "JsonData"
}
{% endhighlight %}
<p>Thus, notifications returned are actually a JsonData array containing pairs of WoopsaValues and PropertyLinks.</p>
<h3 id="multiple-requests-with-the-multirequest-woopsa-method">Multiple Requests with the <samp>MultiRequest</samp> Woopsa method</h3>
<p>When a client needs to make many requests to a Woopsa server, doing them individually can be quite costly in terms of network traffic. The <samp>MultiRequest</samp> method solves this problem by allowing a client to make multiple Woopsa requests at the same time.</p>
<p>A Woopsa server implementing multiple requests will publish a <samp>MultiRequest</samp> function on the root object.</p>
<h4><samp>MultiRequest(JsonData Requests) : JsonData</samp></h4>
<p>Execute the requests specified by <samp>Requests</samp> on the server sequentially.</p>
<dl class="dl-horizontal">
<dt>Arguments:</dt>
<dd>
<ul class="list-unstyled">
<li>
<strong>Requests</strong>: <samp>JsonData</samp>, a list of requests to execute on the server sequentially. The client must attribute a unique ID to each request because the return order of the responses is not garanteed by the JSON serialization. The structure of the <samp>Requests</samp> object must be:
{% highlight javascript %}
[
{
"Id": integer, // A unique ID generated by the client
"Verb": string, // read, write, meta or invoke
"Path": string, // path for this request
"Value": string, // (allowed and required only for write) the value to write to the property specified by path
"Arguments": object // (allowed and required only for invoke) JSON key-value pair of arguments with which to invoke the method
},
/* ... */
]
{% endhighlight %}
</li>
</ul>
</dd>
<dt>Return value:</dt>
<dd>
<samp>JsonData</samp>, An array containing the results of each request specified in <samp>Requests</samp>. The structure of the return value is as follows:
{% highlight javascript %}
[
{
"Id": integer, /* The unique ID specified by the client for the request this is a response to */
"Result": object /* The complete serialization of the response, representing a WoopsaValue in case of success, or an Error otherwise */
},
/* ... */
]
{% endhighlight %}
For methods returning void, "Result" contains the value null.
</dd>
</dl>
<p><strong>Note:</strong> It is the role of the client to ensure the <samp>Id</samp>s are unique. The server should not try to do this verification.</p>