Affen is a requests Session equiped to easily consume plone.restapi
>>> import requests >>> response = requests.get('https://plonedemo.kitconcept.com/@search?sort_on=path', ... headers={'Accept': 'application/json'}, auth=('admin', 'admin')) >>> for i, item in enumerate(response.json()['items']): ... print(i, item['@id']) ... 0 https://plonedemo.kitconcept.com/de 1 https://plonedemo.kitconcept.com/de/Assets 2 https://plonedemo.kitconcept.com/de/demo 3 https://plonedemo.kitconcept.com/de/demo/a-image.jpg 4 https://plonedemo.kitconcept.com/de/demo/big_buck_bunny.mp4 5 https://plonedemo.kitconcept.com/de/demo/ein-link 6 https://plonedemo.kitconcept.com/de/demo/ein-ordner 7 https://plonedemo.kitconcept.com/de/demo/ein-ordner/eine-seite-in-einem-ordner 8 https://plonedemo.kitconcept.com/de/demo/ein-termin 9 https://plonedemo.kitconcept.com/de/demo/eine-nachricht 10 https://plonedemo.kitconcept.com/de/demo/eine-seite 11 https://plonedemo.kitconcept.com/de/demo/ploneconf-plone5.pdf 12 https://plonedemo.kitconcept.com/de/frontpage 13 https://plonedemo.kitconcept.com/en 14 https://plonedemo.kitconcept.com/en/assets 15 https://plonedemo.kitconcept.com/en/demo 16 https://plonedemo.kitconcept.com/en/demo/a-event 17 https://plonedemo.kitconcept.com/en/demo/a-file.pdf 18 https://plonedemo.kitconcept.com/en/demo/a-folder 19 https://plonedemo.kitconcept.com/en/demo/a-folder/a-page-inside-a-folder 20 https://plonedemo.kitconcept.com/en/demo/a-link 21 https://plonedemo.kitconcept.com/en/demo/a-news-item 22 https://plonedemo.kitconcept.com/en/demo/a-page 23 https://plonedemo.kitconcept.com/en/demo/a-photo.jpg 24 https://plonedemo.kitconcept.com/en/demo/a-video.mp4 >>>
And then follow the batching for more.
(and remember to start enumerate at the right number)
>>> response = requests.get(response.json()['batching']['next'], ... headers={'Accept': 'application/json'}, auth=2*('admin',)) >>> for i, item in enumerate(response.json()['items'], start=i + 1): ... print(i, item['@id']) ... 25 https://plonedemo.kitconcept.com/en/frontpage 26 https://plonedemo.kitconcept.com/my-document >>>
An Affen Session can take credentials and an api_root in the contructor, and
has an items function that iterates over anything in restapi that uses the
batching protocol; like Folders, Collectors and restapi endpoints like
@search:
>>> from affen import Session >>> plone = Session('admin', 'admin', 'https://plonedemo.kitconcept.com') >>> for i, item in enumerate(plone.items('@search?sort_on=path')): ... print(i, item['@id']) ... 0 https://plonedemo.kitconcept.com/de 1 https://plonedemo.kitconcept.com/de/Assets 2 https://plonedemo.kitconcept.com/de/demo 3 https://plonedemo.kitconcept.com/de/demo/a-image.jpg 4 https://plonedemo.kitconcept.com/de/demo/big_buck_bunny.mp4 5 https://plonedemo.kitconcept.com/de/demo/ein-link 6 https://plonedemo.kitconcept.com/de/demo/ein-ordner 7 https://plonedemo.kitconcept.com/de/demo/ein-ordner/eine-seite-in-einem-ordner 8 https://plonedemo.kitconcept.com/de/demo/ein-termin 9 https://plonedemo.kitconcept.com/de/demo/eine-nachricht 10 https://plonedemo.kitconcept.com/de/demo/eine-seite 11 https://plonedemo.kitconcept.com/de/demo/ploneconf-plone5.pdf 12 https://plonedemo.kitconcept.com/de/frontpage 13 https://plonedemo.kitconcept.com/en 14 https://plonedemo.kitconcept.com/en/assets 15 https://plonedemo.kitconcept.com/en/demo 16 https://plonedemo.kitconcept.com/en/demo/a-event 17 https://plonedemo.kitconcept.com/en/demo/a-file.pdf 18 https://plonedemo.kitconcept.com/en/demo/a-folder 19 https://plonedemo.kitconcept.com/en/demo/a-folder/a-page-inside-a-folder 20 https://plonedemo.kitconcept.com/en/demo/a-link 21 https://plonedemo.kitconcept.com/en/demo/a-news-item 22 https://plonedemo.kitconcept.com/en/demo/a-page 23 https://plonedemo.kitconcept.com/en/demo/a-photo.jpg 24 https://plonedemo.kitconcept.com/en/demo/a-video.mp4 25 https://plonedemo.kitconcept.com/en/frontpage 26 https://plonedemo.kitconcept.com/my-document >>>
And if you have the permissions, you can read and write to the registry as if it were a dictionary:
>>> plone = Session('admin', 'admin', 'http://127.0.0.1:8080/Plone') >>> plone.registry['plone.allowed_sizes'] ['large 768:768', 'preview 400:400', 'mini 200:200', 'thumb 128:128', 'tile 64:64', 'icon 32:32', 'listing 16:16'] >>> plone.registry['plone.allowed_sizes'] = ['supersize_me 3840:2160'] >>> plone.registry['plone.allowed_sizes'] ['supersize_me 3840:2160'] >>>
>>> vanilla = requests.Session() >>> vanilla.auth = ('admin', 'admin') >>> vanilla.headers['accept'] = 'application/json' >>> ROOT = 'http://127.0.0.1:8080/Plone' >>> # these two lines make it almost as short as Affen... >>> [t['title'] for t in vanilla.get(f'{ROOT}/@types').json()] ['Collection', 'Event', 'File', 'Folder', 'Image', 'Link', 'News Item', 'Page'] >>> # see? f-strings were such a great idea! >>> # Affen is hardly shorter >>> [t['title'] for t in plone.get('@types').json()] ['Collection', 'Event', 'File', 'Folder', 'Image', 'Link', 'News Item', 'Page'] >>>
Sure, until you accidentally reuse the session for requests to a different
host. It's so conveniently close, and seems to behave like requests.get. So
your mypy powered IDE didn't catch it. In fact, it provided handy
autocompletion, so it looked like the Right ThingTM.
>>> vanilla.get('https://httpbin.org/headers').json()['headers']['Authorization'] 'Basic YWRtaW46YWRtaW4=' >>>
OOPS, did we just send our 'Authorization' header to the nice people of httpbin.org? An Affen Session will throw a fit when you try to do that:
>>> plone.get('https://httpbin.org/headers').json() Traceback (most recent call last): ... ValueError: Making requests to other hosts than http://127.0.0.1:8080/Plone/ may leak credentials. Use a different requests.Session for those or change root >>> # and even when whe change the api root >>> plone.root = 'https://httpbin.org' >>> plone.get('headers').json()['headers']['Authorization'] Traceback (most recent call last): ... KeyError: 'Authorization' >>> # it won't send the secrets