~cjwatson/lazr.restful/no-json-binary

« back to all changes in this revision

Viewing changes to src/lazr/restful/docs/webservice.rst

  • Committer: Colin Watson
  • Date: 2020-09-07 12:19:45 UTC
  • mfrom: (265.4.1 py3-doctest-print)
  • Revision ID: cjwatson@canonical.com-20200907121945-16hq299dvhy8dmxq
[r=ilasc] Convert doctests to use print() for Unicode strings.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1096
1096
    >>> response = app(request)
1097
1097
    >>> representation = simplejson.loads(six.text_type(response))
1098
1098
 
1099
 
    >>> representation["authors_collection_link"]
1100
 
    u'http://api.cookbooks.dev/beta/authors'
1101
 
 
1102
 
    >>> representation["cookbooks_collection_link"]
1103
 
    u'http://api.cookbooks.dev/beta/cookbooks'
1104
 
 
1105
 
    >>> representation["dishes_collection_link"]
1106
 
    u'http://api.cookbooks.dev/beta/dishes'
 
1099
    >>> print(representation["authors_collection_link"])
 
1100
    http://api.cookbooks.dev/beta/authors
 
1101
 
 
1102
    >>> print(representation["cookbooks_collection_link"])
 
1103
    http://api.cookbooks.dev/beta/cookbooks
 
1104
 
 
1105
    >>> print(representation["dishes_collection_link"])
 
1106
    http://api.cookbooks.dev/beta/dishes
1107
1107
 
1108
1108
The standard ``absoluteURL()`` function can be used to generate URLs to
1109
1109
content objects published on the web service. It works for the web service
1172
1172
    ...     return simplejson.loads(six.text_type(s))
1173
1173
 
1174
1174
    >>> representation = load_json(collection())
1175
 
    >>> representation['resource_type_link']
1176
 
    u'http://api.cookbooks.dev/beta/#cookbooks'
 
1175
    >>> print(representation['resource_type_link'])
 
1176
    http://api.cookbooks.dev/beta/#cookbooks
1177
1177
 
1178
1178
Pagination
1179
1179
==========
1181
1181
``Collections`` are paginated and served one page at a time. This particular
1182
1182
collection is small enough to fit on one page; it's only got three entries.
1183
1183
 
1184
 
    >>> sorted(representation.keys())
1185
 
    [u'entries', u'resource_type_link', u'start', u'total_size']
 
1184
    >>> for key in sorted(representation.keys()):
 
1185
    ...     print(key)
 
1186
    entries
 
1187
    resource_type_link
 
1188
    start
 
1189
    total_size
1186
1190
    >>> len(representation['entries'])
1187
1191
    3
1188
1192
    >>> representation['total_size']
1196
1200
    >>> collection = request.traverse(app)
1197
1201
    >>> representation = load_json(collection())
1198
1202
 
1199
 
    >>> sorted(representation.keys())
1200
 
    [u'entries', u'next_collection_link', u'resource_type_link',
1201
 
     u'start', u'total_size']
1202
 
    >>> representation['next_collection_link']
1203
 
    u'http://api.cookbooks.dev/beta/cookbooks?ws.size=2&memo=2&ws.start=2'
 
1203
    >>> for key in sorted(representation.keys()):
 
1204
    ...     print(key)
 
1205
    entries
 
1206
    next_collection_link
 
1207
    resource_type_link
 
1208
    start
 
1209
    total_size
 
1210
    >>> print(representation['next_collection_link'])
 
1211
    http://api.cookbooks.dev/beta/cookbooks?ws.size=2&memo=2&ws.start=2
1204
1212
    >>> len(representation['entries'])
1205
1213
    2
1206
1214
    >>> representation['total_size']
1215
1223
    >>> collection = request.traverse(app)
1216
1224
    >>> representation = load_json(collection())
1217
1225
 
1218
 
    >>> sorted(representation.keys())
1219
 
    [u'entries', u'prev_collection_link', u'resource_type_link',
1220
 
     u'start', u'total_size']
1221
 
    >>> representation['prev_collection_link']
1222
 
    u'http://api.cookbooks.dev/beta/cookbooks?ws.size=2&direction=backwards&memo=2'
 
1226
    >>> for key in sorted(representation.keys()):
 
1227
    ...     print(key)
 
1228
    entries
 
1229
    prev_collection_link
 
1230
    resource_type_link
 
1231
    start
 
1232
    total_size
 
1233
    >>> print(representation['prev_collection_link'])
 
1234
    http://api.cookbooks.dev/beta/cookbooks?ws.size=2&direction=backwards&memo=2
1223
1235
    >>> len(representation['entries'])
1224
1236
    1
1225
1237
 
1237
1249
    ...             'ws.op=find_recipes&name=Roast%20chicken'})
1238
1250
    >>> operation_resource = request.traverse(app)
1239
1251
    >>> chicken_recipes = load_json(operation_resource())
1240
 
    >>> sorted([c['instructions'] for c in chicken_recipes['entries']])
1241
 
    [u'A perfectly roasted chicken is...',
1242
 
     u'Draw, singe, stuff, and truss...',
1243
 
     u'You can always judge...']
 
1252
    >>> for instruction in sorted(
 
1253
    ...         [c['instructions'] for c in chicken_recipes['entries']]):
 
1254
    ...     print(instruction)
 
1255
    A perfectly roasted chicken is...
 
1256
    Draw, singe, stuff, and truss...
 
1257
    You can always judge...
1244
1258
 
1245
1259
Custom operations may include custom error checking. Error messages
1246
1260
are passed along to the client.
1309
1323
    >>> path = '/beta/authors/Deborah%20Madison'
1310
1324
    >>> request = create_web_service_request(path)
1311
1325
    >>> author = request.traverse(app)
1312
 
    >>> load_json(author())['name']
1313
 
    u'Deborah Madison'
 
1326
    >>> print(load_json(author())['name'])
 
1327
    Deborah Madison
1314
1328
 
1315
1329
Here we modify a cookbook's cuisine using a named operation. Because
1316
1330
this operation's definition does set send_modified_event to True, an
1329
1343
    >>> path = '/beta/cookbooks/Everyday%20Greens'
1330
1344
    >>> request = create_web_service_request(path)
1331
1345
    >>> cookbook = request.traverse(app)
1332
 
    >>> load_json(cookbook())['cuisine']
1333
 
    u'Nouvelle Brazilian'
 
1346
    >>> print(load_json(cookbook())['cuisine'])
 
1347
    Nouvelle Brazilian
1334
1348
 
1335
1349
 
1336
1350
Entry resources
1346
1360
    >>> representation = load_json(collection())
1347
1361
    >>> entries = sorted(representation['entries'],
1348
1362
    ...                  key=operator.itemgetter('name'))
1349
 
    >>> entries[0]['self_link']
1350
 
    u'http://api.cookbooks.dev/beta/cookbooks/Beard%20on%20Bread'
 
1363
    >>> print(entries[0]['self_link'])
 
1364
    http://api.cookbooks.dev/beta/cookbooks/Beard%20on%20Bread
1351
1365
 
1352
1366
Regular data fields are exposed with their given names. The 'name'
1353
1367
field stays 'name'.
1354
1368
 
1355
 
    >>> entries[0]['name']
1356
 
    u'Beard on Bread'
 
1369
    >>> print(entries[0]['name'])
 
1370
    Beard on Bread
1357
1371
 
1358
1372
Fields that are references to other objects -- ``Object``, ``Reference``, and
1359
1373
``ReferenceChoice`` -- are exposed as links to those objects. Each cookbook
1360
1374
has such a link to its author.
1361
1375
 
1362
 
    >>> entries[0]['author_link']
1363
 
    u'http://api.cookbooks.dev/beta/authors/James%20Beard'
 
1376
    >>> print(entries[0]['author_link'])
 
1377
    http://api.cookbooks.dev/beta/authors/James%20Beard
1364
1378
 
1365
1379
Fields that are references to externally hosted files (Bytes) are also
1366
1380
exposed as links to those files. Each cookbook has such a link to its
1367
1381
cover image.
1368
1382
 
1369
 
    >>> entries[0]['cover_link']
1370
 
    u'http://api.cookbooks.dev/beta/cookbooks/Beard%20on%20Bread/cover'
 
1383
    >>> print(entries[0]['cover_link'])
 
1384
    http://api.cookbooks.dev/beta/cookbooks/Beard%20on%20Bread/cover
1371
1385
 
1372
1386
Fields that are references to collections of objects are exposed as
1373
1387
links to those collections. Each cookbook has such a link to its
1374
1388
recipes.
1375
1389
 
1376
 
    >>> entries[0]['recipes_collection_link']
1377
 
    u'http://api.cookbooks.dev/beta/cookbooks/Beard%20on%20Bread/recipes'
 
1390
    >>> print(entries[0]['recipes_collection_link'])
 
1391
    http://api.cookbooks.dev/beta/cookbooks/Beard%20on%20Bread/recipes
1378
1392
 
1379
1393
Calling the ``CollectionResource`` object makes it process the incoming
1380
1394
request. Since this is a GET request, calling the resource publishes the
1449
1463
    >>> comments_resource = request.traverse(app)
1450
1464
 
1451
1465
    >>> comments = load_json(comments_resource())
1452
 
    >>> [c['text'] for c in comments['entries']]
1453
 
    [u'Clear and concise.']
 
1466
    >>> for c in comments['entries']:
 
1467
    ...     print(c['text'])
 
1468
    Clear and concise.
1454
1469
 
1455
1470
    >>> request = create_web_service_request(
1456
1471
    ...     roast_chicken_comments_url + '/1')
1472
1487
    ...     environ={'QUERY_STRING' : 'ws.op=find_similar_recipes'})
1473
1488
    >>> operation_resource = request.traverse(app)
1474
1489
    >>> chicken_recipes = load_json(operation_resource())
1475
 
    >>> sorted([c['instructions'] for c in chicken_recipes['entries']])
1476
 
    [u'A perfectly roasted chicken is...',
1477
 
     u'Draw, singe, stuff, and truss...',
1478
 
     u'You can always judge...']
 
1490
    >>> for instruction in sorted(
 
1491
    ...         [c['instructions'] for c in chicken_recipes['entries']]):
 
1492
    ...     print(instruction)
 
1493
    A perfectly roasted chicken is...
 
1494
    Draw, singe, stuff, and truss...
 
1495
    You can always judge...
1479
1496
 
1480
1497
Named operation return values
1481
1498
=============================
1793
1810
The author's name is public information, so it's visible. But the link
1794
1811
to his favorite recipe has been redacted.
1795
1812
 
1796
 
    >>> author['name']
1797
 
    u'James Beard'
1798
 
    >>> author['favorite_recipe_link']
1799
 
    u'tag:launchpad.net:2008:redacted'
 
1813
    >>> print(author['name'])
 
1814
    James Beard
 
1815
    >>> print(author['favorite_recipe_link'])
 
1816
    tag:launchpad.net:2008:redacted
1800
1817
 
1801
1818
It's possible to use a representation that contains redacted
1802
1819
information when sending a PUT or PATCH request back to the
1829
1846
    >>> get_request = create_web_service_request(beard_url)
1830
1847
    >>> author_resource = get_request.traverse(app)
1831
1848
    >>> author = load_json(author_resource())
1832
 
    >>> author['favorite_recipe_link']
1833
 
    u'http://api.cookbooks.dev/beta/cookbooks/The%20Joy%20of%20Cooking/recipes/Roast%20chicken'
 
1849
    >>> print(author['favorite_recipe_link'])
 
1850
    http://api.cookbooks.dev/beta/cookbooks/The%20Joy%20of%20Cooking/recipes/Roast%20chicken
1834
1851
 
1835
1852
Finally, you can't set an attribute to a value that you wouldn't have
1836
1853
permission to see:
1991
2008
    ...     '/beta/cookbooks/The%20Joy%20of%20Cooking%20%28revised%29')
1992
2009
    >>> joy_resource = request.traverse(app)
1993
2010
    >>> joy = load_json(joy_resource())
1994
 
    >>> joy['name']
1995
 
    u'The Joy of Cooking (revised)'
 
2011
    >>> print(joy['name'])
 
2012
    The Joy of Cooking (revised)
1996
2013
 
1997
2014
An entry that responds to PATCH will also respond to PUT. With PUT you
1998
2015
modify the document you got in response to a GET request, and send the
2032
2049
    >>> joy_resource = create_web_service_request(
2033
2050
    ...     '/beta/cookbooks/The%20Joy%20of%20Cooking').traverse(app)
2034
2051
    >>> joy = load_json(joy_resource())
2035
 
    >>> joy['name']
2036
 
    u'The Joy of Cooking'
 
2052
    >>> print(joy['name'])
 
2053
    The Joy of Cooking
2037
2054
 
2038
2055
It's also possible to change the relationships between objects. Here,
2039
2056
we change a cookbook's author. Since all objects are identified by
2055
2072
    >>> joy_resource = create_web_service_request(
2056
2073
    ...     '/beta/cookbooks/The%20Joy%20of%20Cooking').traverse(app)
2057
2074
    >>> joy = load_json(joy_resource())
2058
 
    >>> joy['author_link']
2059
 
    u'http://api.cookbooks.dev/beta/authors/Julia%20Child'
 
2075
    >>> print(joy['author_link'])
 
2076
    http://api.cookbooks.dev/beta/authors/Julia%20Child
2060
2077
 
2061
2078
When identifying an object by URL, make sure the hostname of your URL
2062
2079
matches the hostname you're requesting. If they don't match, your