comparison doc/rest.txt @ 5695:3e1b66c4e1e2

Update docs. Correct errors reported by setup.py build_docs. Add rest interface and link to rest doc to features page. Add link to xmlrpc doc to features page. Add rest doc to index. Update rest doc, hopefully clarify confusing use of parameters in patch action section. Fix code examples in "Adding new rest endpoints" section. Fix example adding import of exception.
author John Rouillard <rouilj@ieee.org>
date Sun, 07 Apr 2019 20:17:52 -0400
parents 1b9ef04b9528
children c7dd1cae3416
comparison
equal deleted inserted replaced
5694:c3ffa1ef6b7f 5695:3e1b66c4e1e2
34 from relative REST-API links for brevety. 34 from relative REST-API links for brevety.
35 35
36 Summary 36 Summary
37 ======= 37 =======
38 38
39 A Summary page can be reached via ``/data/summary`` via the ``GET`` method. 39 A Summary page can be reached via ``/summary`` via the ``GET`` method.
40 This is currently hard-coded for the standard tracker schema shipped 40 This is currently hard-coded for the standard tracker schema shipped
41 with roundup and will display a summary of open issues. 41 with roundup and will display a summary of open issues.
42 42
43 Data 43 Data
44 ==== 44 ====
134 list. The same effect can be achieved with a ``PUT`` request and an 134 list. The same effect can be achieved with a ``PUT`` request and an
135 empty new value. 135 empty new value.
136 136
137 Finally the ``PATCH`` method can be applied to individual items, e.g., 137 Finally the ``PATCH`` method can be applied to individual items, e.g.,
138 ``/data/issue/42`` and to properties, e.g., ``/data/issue/42/title``. 138 ``/data/issue/42`` and to properties, e.g., ``/data/issue/42/title``.
139 This method gets an operator ``@op=<method>`` where ``<method`` is one 139 This method gets an operator ``@op=<method>`` where ``<method>`` is one
140 of ``add``, ``replace``, ``remove``, only for an item (not for a 140 of ``add``, ``replace``, ``remove``, only for an item (not for a
141 property) an additional operator ``action`` is supported. If no operator 141 property) an additional operator ``action`` is supported. If no operator
142 is specified, the default is ``replace``. The first three operators are 142 is specified, the default is ``replace``. The first three operators are
143 self explanatory. For an ``action`` operator an ``@action_name`` and 143 self explanatory. For an ``action`` operator an ``@action_name`` and
144 optional ``@action_argsXXX`` parameters have to be supplied. Currently 144 optional ``@action_argsXXX`` parameters have to be supplied. Currently
145 there are only two actions without parameters, namely ``retire`` and 145 there are only two actions, neither has args, namely ``retire`` and
146 ``restore``. The ``retire`` action on an item is the same as a 146 ``restore``. The ``retire`` action on an item is the same as a
147 ``DELETE`` method, it retires the item. The ``restore`` action is the 147 ``DELETE`` method, it retires the item. The ``restore`` action is the
148 inverse of ``retire``, the item is again visible. 148 inverse of ``retire``, the item is again visible.
149 On success the returned value is the same as the respective ``GET`` 149 On success the returned value is the same as the respective ``GET``
150 method. 150 method.
196 ========================= 196 =========================
197 197
198 Add or edit the file interfaces.py at the root of the tracker 198 Add or edit the file interfaces.py at the root of the tracker
199 directory. 199 directory.
200 200
201 In that file add (remove indentation): 201 In that file add::
202 202
203 from roundup.rest import Routing, RestfulInstance, _data_decorator 203 from roundup.rest import Routing, RestfulInstance, _data_decorator
204 from roundup.exceptions import Unauthorised
204 205
205 class RestfulInstance: 206 class RestfulInstance:
206 207
207 @Routing.route("/summary2") 208 @Routing.route("/summary2")
208 @_data_decorator 209 @_data_decorator
209 def summary2(self, input): 210 def summary2(self, input):
210 result = { "hello": "world" } 211 result = { "hello": "world" }
211 return 200, result 212 return 200, result
212 213
213 will make a new endpoint .../rest/summary2 that you can test with: 214 will make a new endpoint .../rest/summary2 that you can test with::
214 215
215 $ curl -X GET .../rest/summary2 216 $ curl -X GET .../rest/summary2
216 { 217 {
217 "data": { 218 "data": {
218 "hello": "world" 219 "hello": "world"
219 } 220 }
220 } 221 }
221 222
222 Similarly appending this to interfaces.py after summary2: 223 Similarly appending this to interfaces.py after summary2::
223 224
224 @Routing.route("/data/<:class_name>/@schema", 'GET') 225 # handle more endpoints
225 def get_element_schema(self, class_name, input): 226 @Routing.route("/data/<:class_name>/@schema", 'GET')
226 result = { "schema": {} } 227 def get_element_schema(self, class_name, input):
227 uid = self.db.getuid () 228 result = { "schema": {} }
228 if not self.db.security.hasPermission('View', uid, class_name) : 229 uid = self.db.getuid ()
229 raise Unauthorised('Permission to view %s denied' % class_name) 230 if not self.db.security.hasPermission('View', uid, class_name) :
230 231 raise Unauthorised('Permission to view %s denied' % class_name)
231 class_obj = self.db.getclass(class_name) 232
232 props = class_obj.getprops(protected=False) 233 class_obj = self.db.getclass(class_name)
233 schema = result['schema'] 234 props = class_obj.getprops(protected=False)
234 235 schema = result['schema']
235 for prop in props: 236
236 schema[prop] = { "type": repr(class_obj.properties[prop]) } 237 for prop in props:
237 238 schema[prop] = { "type": repr(class_obj.properties[prop]) }
238 return result 239
239 240 return result
240 returns some data about the class 241
242 ..
243 the # comment in the example is needed to preserve indention under Class.
244
245 returns some data about the class::
241 246
242 $ curl -X GET .../rest/data/issue/@schema 247 $ curl -X GET .../rest/data/issue/@schema
243 { 248 {
244 "schema": { 249 "schema": {
245 "keyword": { 250 "keyword": {
256 }, ... 261 }, ...
257 } 262 }
258 } 263 }
259 264
260 265
266 Adding other endpoints (e.g. to allow an OPTIONS query against
267 ``/data/issue/@schema``) is left as an exercise for the reader.
268
261 Searches and selection 269 Searches and selection
262 ====================== 270 ======================
263 271
264 One difficult interface issue is selection of items from a long list. 272 One difficult interface issue is selection of items from a long list.
265 Using multi-item selects requires loading a lot of data (e.g. consider 273 Using multi-item selects requires loading a lot of data (e.g. consider
269 This can be made easier using javascript selection tools like select2, 277 This can be made easier using javascript selection tools like select2,
270 selectize.js, chosen etc. These tools can query a remote data provider 278 selectize.js, chosen etc. These tools can query a remote data provider
271 to get a list of items for the user to select from. 279 to get a list of items for the user to select from.
272 280
273 Consider a multi-select box for the superseder property. Using 281 Consider a multi-select box for the superseder property. Using
274 selectize.js (and jquery) code similar to: 282 selectize.js (and jquery) code similar to::
275 283
276 $('#superseder').selectize({ 284 $('#superseder').selectize({
277 valueField: 'id', 285 valueField: 'id',
278 labelField: 'title', 286 labelField: 'title',
279 searchField: 'title', ... 287 searchField: 'title', ...
280 load: function(query, callback) { 288 load: function(query, callback) {
281 if (!query.length) return callback(); 289 if (!query.length) return callback();
287 success: function(res) { 295 success: function(res) {
288 callback(res.data.collection);} 296 callback(res.data.collection);}
289 297
290 Sets up a box that a user can type the word "request" into. Then 298 Sets up a box that a user can type the word "request" into. Then
291 selectize.js will use that word to generate an ajax request with the 299 selectize.js will use that word to generate an ajax request with the
292 url: .../rest/data/issue?@verbose=2&title=request 300 url: ``.../rest/data/issue?@verbose=2&title=request``
293 301
294 This will return data like: 302 This will return data like::
295 303
296 { 304 {
297 "data": { 305 "data": {
298 "@total_size": 440, 306 "@total_size": 440,
299 "collection": [ 307 "collection": [
305 { 313 {
306 "link": ".../rest/data/issue/27", 314 "link": ".../rest/data/issue/27",
307 "id": "27", 315 "id": "27",
308 "title": "Request for foo" 316 "title": "Request for foo"
309 }, 317 },
318 ...
310 319
311 selectize.js will look at these objects (as passed to 320 selectize.js will look at these objects (as passed to
312 callback(res.data.collection)) and create a select list from the each 321 callback(res.data.collection)) and create a select list from the each
313 object showing the user the labelField (title) for each object and 322 object showing the user the labelField (title) for each object and
314 associating each title with the corresponding valueField (id). The 323 associating each title with the corresponding valueField (id). The
315 example above has 440 issues returned from a total of 2000 324 example above has 440 issues returned from a total of 2000
316 issues. Only 440 had the word "request" somewhere in the title greatly 325 issues. Only 440 had the word "request" somewhere in the title greatly
317 reducing the amount of data that needed to be transferred. 326 reducing the amount of data that needed to be transferred.
318 327
319 Similar things can be set up to search a large list of keywords using 328 Similar code can be set up to search a large list of keywords using::
320 329
321 .../rest/data/keyword?@verbose=2&name=some 330 .../rest/data/keyword?@verbose=2&name=some
322 331
323 which would return: "some keyword" "awesome" "somebody" making 332 which would return: "some keyword" "awesome" "somebody" making
324 selections for links and multilinks much easier. 333 selections for links and multilinks much easier.
325 334
326 Hopefully future enhancements will allow get on a collection to 335 Hopefully future enhancements will allow get on a collection to
327 include other fields. Why do we want this? Selectize.js can set up 336 include other fields. Why do we want this? Selectize.js can set up
328 option groups (optgroups) in the select pulldown. So by including 337 option groups (optgroups) in the select pulldown. So by including
329 status in the returned data: 338 status in the returned data::
330 339
331 { 340 {
332 "link": ".../rest/data/issue/27", 341 "link": ".../rest/data/issue/27",
333 "id": "27", 342 "id": "27",
334 "title": "Request for foo", 343 "title": "Request for foo",
335 'status": "open" 344 'status": "open"
336 }, 345 },
337 346
338 a select widget like: 347 a select widget like::
339 348
340 === New === 349 === New ===
341 A request 350 A request
342 === Open === 351 === Open ===
343 Request for bar 352 Request for bar

Roundup Issue Tracker: http://roundup-tracker.org/