1096
1096
>>> response = app(request)
1097
1097
>>> representation = simplejson.loads(six.text_type(response))
1099
>>> representation["authors_collection_link"]
1100
u'http://api.cookbooks.dev/beta/authors'
1102
>>> representation["cookbooks_collection_link"]
1103
u'http://api.cookbooks.dev/beta/cookbooks'
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
1102
>>> print(representation["cookbooks_collection_link"])
1103
http://api.cookbooks.dev/beta/cookbooks
1105
>>> print(representation["dishes_collection_link"])
1106
http://api.cookbooks.dev/beta/dishes
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))
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
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.
1184
>>> sorted(representation.keys())
1185
[u'entries', u'resource_type_link', u'start', u'total_size']
1184
>>> for key in sorted(representation.keys()):
1186
1190
>>> len(representation['entries'])
1188
1192
>>> representation['total_size']
1196
1200
>>> collection = request.traverse(app)
1197
1201
>>> representation = load_json(collection())
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()):
1206
next_collection_link
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'])
1206
1214
>>> representation['total_size']
1215
1223
>>> collection = request.traverse(app)
1216
1224
>>> representation = load_json(collection())
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()):
1229
prev_collection_link
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'])
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...
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']
1326
>>> print(load_json(author())['name'])
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'])
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
1352
1366
Regular data fields are exposed with their given names. The 'name'
1353
1367
field stays 'name'.
1355
>>> entries[0]['name']
1369
>>> print(entries[0]['name'])
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.
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
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
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
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
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
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)
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'])
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...
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.
1798
>>> author['favorite_recipe_link']
1799
u'tag:launchpad.net:2008:redacted'
1813
>>> print(author['name'])
1815
>>> print(author['favorite_recipe_link'])
1816
tag:launchpad.net:2008:redacted
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
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())
1995
u'The Joy of Cooking (revised)'
2011
>>> print(joy['name'])
2012
The Joy of Cooking (revised)
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())
2036
u'The Joy of Cooking'
2052
>>> print(joy['name'])
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
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