Data structures
===============

A data returned from successful API calls are transformed and presented to you as a [Munch](https://github.com/Infinidat/munch) (it is a subclass of a
``dict`` with all its features, with an additional support of accessing its attributes like object properties, etc).
This page shows how to work with the results, how to access the original responses from frontend and what are the
specifics for lists of results.


First, let's just initialize an API client, and obtain some object (in this example a build) to be examined.

::

    from copr.v3 import Client
    from pprint import pprint
    client = Client.create_from_config_file()
    build = client.build_proxy.get(2545)
    pprint(build)


As advertised, the data is represented as a [Munch](https://github.com/Infinidat/munch).

::

    Munch({u'source_package': {u'url': u'http://backend/results//@copr/copr/srpm-builds/00002545/ed-1.14.2-3.fc26.src.rpm', u'version': u'1.14.2-3.fc26', u'name': u'ed'}, '__response__': <Response [200]>, u'projectname': u'copr', u'started_on': 1526406595, u'submitted_on': 1525635534, u'state': u'succeeded', u'ended_on': 1526408106, u'ownername': u'@copr', u'repo_url': u'http://backend/results/@copr/copr', u'submitter': u'frostyx', u'chroots': [u'fedora-27-x86_64', u'fedora-rawhide-x86_64'], u'id': 2545})


What exactly it is? It is a structure that extends ``dict`` and add more features to it.


::

    print(type(build))
    print(isinstance(build, dict))

::

    <class 'munch.Munch'>
    True


In the first example, it is quite hard to see, what attributes are available and what are their values. It is possible
to print the structure in more human-readable format.


::

    from pprint import pprint
    pprint(dict(build))

::

    {'__response__': <Response [200]>,
     u'chroots': [u'fedora-27-x86_64', u'fedora-rawhide-x86_64'],
     u'ended_on': 1526408106,
     u'id': 2545,
     u'ownername': u'@copr',
     u'projectname': u'copr',
     u'repo_url': u'http://backend/results/@copr/foo',
     u'source_package': {u'name': u'ed',
                         u'url': u'http://backend/results//@copr/copr/srpm-builds/00002545/ed-1.14.2-3.fc26.src.rpm',
                         u'version': u'1.14.2-3.fc26'},
     u'started_on': 1526406595,
     u'state': u'succeeded',
     u'submitted_on': 1525635534,
     u'submitter': u'frostyx'}


Attributes are accessible through standard dict bracket-style, but also through object like property-style.


::

    assert build.ownername == build["ownername"]
    print(build.ownername)

::

    @copr


Every data munch also stores the original response from frontend.

::

    print(build.__response__)
    print(type(build.__response__))
    print(build.__response__.status_code)

::

    <Response [200]>
    requests.models.Response
    200


Lists of objects
----------------

Now, it should be clear how a single data object is represented. Let's see how the situation looks like
when multiple objects are returned.

::

    builds = client.build_proxy.get_list("@copr", "copr")
    print(builds)


At the first sight, it is just a list of munches.

::

    [Munch({u'source_package': {u'url': u'http://backend/results//@copr/copr/srpm-builds/00002544/mksh-56c-3.fc26.src.rpm', u'version': u'56c-3.fc26', u'name': u'mksh'}, u'projectname': u'copr', u'started_on': 1519063348, u'submitted_on': 1519062565, u'state': u'succeeded', u'ended_on': 1519064069, u'ownername': u'frostyx', u'repo_url': u'http://backend/results/@copr/copr', u'submitter': u'frostyx', u'chroots': [u'fedora-rawhide-i386', u'fedora-rawhide-x86_64'], u'id': 2544}),
     Munch({u'source_package': {u'url': u'http://backend/results//@copr/copr/srpm-builds/00002545/ed-1.14.2-3.fc26.src.rpm', u'version': u'1.14.2-3.fc26', u'name': u'ed'}, u'projectname': u'copr', u'started_on': 1526406595, u'submitted_on': 1525635534, u'state': u'succeeded', u'ended_on': 1526408106, u'ownername': u'@copr', u'repo_url': u'http://backend/results/@copr/copr', u'submitter': u'frostyx', u'chroots': [u'fedora-27-x86_64', u'fedora-rawhide-x86_64'], u'id': 2545})]


Not exactly. It is a subclass of a ``list`` created in the ``copr`` package.


::

    print(type(builds))
    print(isinstance(builds, list))

::

    <class 'copr.v3.helpers.List'>
    True

Let's answer the anticipated question, why do we need a modified implementation of a list. It can provide the frontend
response in a ``__response__`` attribute the same way that single munch does.

::

    print(builds.__response__)
    <Response [200]>


It also provides a ``meta`` attribute, that has information about ordering results in the list and possibly limiting
their number. Please read more about the pagination.

::

    print(builds.meta)
    Munch({u'offset': 0, u'limit': None, u'order_type': u'ASC', u'order': u'id'})


Iterating through all objects in the response looks as expected.

::

    for build in builds:
        print("Build {} {}".format(build.id, build.state))

::

    Build 2544 succeeded
    Build 2545 succeeded


Modifying data
--------------

Previous examples show the data structures when the object was explicitly queried
(i.e. ``get`` or ``get_list`` method was used). It remains to be explained, how the responses look like when a user
tries to add, modify, or delete some object. Simply enough, the operation is executed and the object is implicitly
queried afterward.


::

    build = client.build_proxy.delete(2545)
    print(build)

::

    Munch({u'source_package': {u'url': u'http://backend/results//@copr/copr/srpm-builds/00002545/ed-1.14.2-3.fc26.src.rpm', u'version': u'1.14.2-3.fc26', u'name': u'ed'}, u'projectname': u'copr', u'started_on': 1526406595, u'submitted_on': 1525635534, u'state': u'succeeded', u'ended_on': 1526408106, u'ownername': u'@copr', u'repo_url': u'http://backend/results/@copr/copr', u'submitter': u'frostyx', u'chroots': [u'fedora-27-x86_64', u'fedora-rawhide-x86_64'], u'id': 2545})


The object was deleted, so it obviously can't be queried one more time

::

    client.build_proxy.get(build.id)

::

    CoprNoResultException: Build 2545 does not exist.
