[Pkg-puppet-devel] [python-pypuppetdb] 01/02: Imported Upstream version 0.1.1+git080614

Jonas Genannt jonas at brachium-system.net
Wed Aug 6 17:37:27 UTC 2014


This is an automated email from the git hooks/post-receive script.

hggh-guest pushed a commit to branch master
in repository python-pypuppetdb.

commit 74f2f13fe95ba96b10639e8f6833e35e77ec34d5
Author: Jonas Genannt <jonas at brachium-system.net>
Date:   Wed Aug 6 19:31:22 2014 +0200

    Imported Upstream version 0.1.1+git080614
---
 .coveragerc                              |   3 +
 .gitignore                               |  50 ++++
 .travis.yml                              |  13 ++
 PKG-INFO                                 | 378 -------------------------------
 README.rst                               |  45 ++++
 conftest.py                              |  26 +++
 docs/Makefile                            | 153 +++++++++++++
 docs/api.rst                             | 128 +++++++++++
 docs/conf.py                             |  60 +++++
 docs/index.rst                           |  37 +++
 docs/make.bat                            | 190 ++++++++++++++++
 docs/quickstart.rst                      | 133 +++++++++++
 pypuppetdb.egg-info/PKG-INFO             | 378 -------------------------------
 pypuppetdb.egg-info/SOURCES.txt          |  19 --
 pypuppetdb.egg-info/dependency_links.txt |   1 -
 pypuppetdb.egg-info/requires.txt         |   1 -
 pypuppetdb.egg-info/top_level.txt        |   1 -
 pypuppetdb/api/__init__.py               |  28 ++-
 pypuppetdb/api/v2.py                     |   6 +-
 pypuppetdb/api/v3.py                     |  20 +-
 pypuppetdb/types.py                      |  17 +-
 python-pypuppetdb.spec                   |  48 ++++
 setup.cfg                                |  10 +-
 setup.py                                 |   4 +-
 test-requirements.txt                    |   6 +
 tests/test_baseapi.py                    | 261 +++++++++++++++++++++
 tests/test_connect.py                    |  17 ++
 tests/test_package.py                    |  11 +
 tests/test_types.py                      | 185 +++++++++++++++
 tests/test_utils.py                      |  49 ++++
 tox.ini                                  |  11 +
 31 files changed, 1475 insertions(+), 814 deletions(-)

diff --git a/.coveragerc b/.coveragerc
new file mode 100644
index 0000000..4f627e1
--- /dev/null
+++ b/.coveragerc
@@ -0,0 +1,3 @@
+[report]
+exclude_lines =
+    pragma: notest
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..a845636
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,50 @@
+*.py[cod]
+
+# C extensions
+*.so
+
+# Pytest
+.cache
+__pycache__
+
+# Packages
+*.egg
+*.egg-info
+dist
+build
+eggs
+parts
+bin
+var
+sdist
+develop-eggs
+.installed.cfg
+lib
+lib64
+
+# Installer logs
+pip-log.txt
+
+# Unit test / coverage reports
+.coverage
+.tox
+nosetests.xml
+htmlcov
+
+# Translations
+*.mo
+
+# Virtualenv / pyenv
+.python-version
+.venv
+
+# Sphinx
+docs/_build
+
+# Mr Developer
+.mr.developer.cfg
+.project
+.pydevproject
+
+# OS X
+.DS_*
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..10eb870
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,13 @@
+language: python
+python:
+  - 2.6
+  - 2.7
+  - 3.3
+install:
+  - pip install -q -r test-requirements.txt --use-wheel
+  - pip install -q coverage coveralls --use-wheel
+script: coverage run --source pypuppetdb -m py.test --pep8
+after_success:
+  - coveralls
+notifications:
+  email: false
diff --git a/PKG-INFO b/PKG-INFO
deleted file mode 100644
index 4e78d5b..0000000
--- a/PKG-INFO
+++ /dev/null
@@ -1,378 +0,0 @@
-Metadata-Version: 1.1
-Name: pypuppetdb
-Version: 0.1.1
-Summary: Library for working with the PuppetDB REST API.
-Home-page: https://github.com/nedap/pypuppetdb
-Author: Daniele Sluijters
-Author-email: daniele.sluijters+pypi at gmail.com
-License: Apache License 2.0
-Description: ##########
-        pypuppetdb
-        ##########
-        
-        .. image:: https://api.travis-ci.org/nedap/pypuppetdb.png
-           :target: https://travis-ci.org/nedap/pypuppetdb
-        
-        .. image:: https://coveralls.io/repos/nedap/pypuppetdb/badge.png
-           :target: https://coeralls.io/r/nedap/pypuppetdb
-        
-        .. image:: https://pypip.in/d/pypuppetdb/badge.png
-           :target: https://crate.io/packages/pypuppetdb
-        
-        .. image:: https://pypip.in/v/pypuppetdb/badge.png
-           :target: https://crate.io/packages/pypuppetdb
-        
-        pypuppetdtb is a library to work with PuppetDB's REST API. It is implemented
-        using the `requests`_ library.
-        
-        .. _requests: http://docs.python-requests.org/en/latest/
-        
-        This library is a thin wrapper around the REST API providing some convinience
-        functions and objects to request and hold data from PuppetDB.
-        
-        To use this library you will need:
-            * Python 2.6 or 2.7
-            * Python 3.3
-        
-        Installation
-        ============
-        
-        You can install this package from source or from PyPi.
-        
-        .. code-block:: bash
-        
-           $ pip install pypuppetdb
-        
-        .. code-block:: bash
-        
-           $ git clone https://github.com/nedap/pypuppetdb
-           $ python setup.py install
-        
-        If you wish to hack on it clone the repository but after that run:
-        
-        .. code-block:: bash
-        
-           $ pip install -r requirements.txt
-        
-        This will install all the runtime requirements of pypuppetdb and the
-        dependencies for the test suite and generation of documentation.
-        
-        Usage
-        =====
-        
-        Once you have pypuppetdb installed you can configure it to connect to PuppetDB
-        and take it from there.
-        
-        Connecting
-        ----------
-        
-        The first thing you need to do is to connect with PuppetDB:
-        
-        .. code-block:: python
-        
-           >>> from pypuppetdb import connect
-           >>> db = connect()
-        
-        Nodes
-        -----
-        
-        The following will return a generator object yielding Node objects for every
-        returned node from PuppetDB.
-        
-        .. code-block:: python
-        
-           >>> nodes = db.nodes()
-           >>> for node in nodes:
-           >>>   print(node)
-           host1
-           host2
-           ...
-        
-        To query a single node the singular `node()` can be used:
-        
-        .. code-block:: python
-        
-            >>> node = db.node('hostname')
-            >>> print(node)
-            hostname
-        
-        Node scope
-        ~~~~~~~~~~
-        
-        The Node objects are a bit more special in that they can query for facts and
-        resources themselves. Using those methods from a node object will automatically
-        add a query to the request scoping the request to the node.
-        
-        .. code-block:: python
-        
-           >>> node = db.node('hostname')
-           >>> print(node.fact('osfamily'))
-           osfamily/hostname
-        
-        Facts
-        -----
-        
-        .. code-block:: python
-        
-           >>> facts = db.facts('osfamily')
-           >>> for fact in facts:
-           >>>   print(fact)
-           osfamily/host1
-           osfamily/host2
-        
-        That queries PuppetDB for the 'osfamily' fact and will yield Fact objects,
-        one per node this fact is known for.
-        
-        Resources
-        ---------
-        
-        .. code-block:: python
-        
-           >>> resources = db.resources('file')
-        
-        Will return a generator object containing all file resources you're managing
-        across your infrastructure. This is probably a bad idea if you have a big
-        number of nodes as the response will be huge.
-        
-        Catalogs
-        ---------
-        
-        .. code-block:: python
-        
-           >>> catalog = db.catalog('hostname')
-           >>> for res in catalog.get_resources():
-           >>>     print(res)
-        
-        Will return a Catalog object with the latest Catalog of the definded host. This
-        catalog contains the defined Resources and Edges.
-        
-        Getting Help
-        ============
-        This project is still very new so it's not inconceivable you'll run into
-        issues.
-        
-        For bug reports you can file an `issue`_. If you need help with something
-        feel free to hit up `@daenney`_ by e-mail or find him on IRC. He can usually
-        be found on `IRCnet`_ and `Freenode`_ and idles in #puppet.
-        
-        There's now also the #puppetboard channel on `Freenode`_ where we hang out
-        and answer questions related to pypuppetdb and Puppetboard.
-        
-        .. _issue: https://github.com/nedap/pypuppetdb/issues
-        .. _ at daenney: https://github.com/daenney
-        .. _IRCnet: http://www.ircnet.org
-        .. _Freenode: http://freenode.net
-        
-        Documentation
-        =============
-        API documentation is automatically generated from the docstrings using
-        Sphinx's autodoc feature. 
-        
-        Documentation will automatically be rebuilt on every push thanks to the
-        Read The Docs webhook. You can `find it here`_.
-        
-        .. _find it here: https://pypuppetdb.readthedocs.org/en/latest/
-        
-        You can build the documentation manually by doing:
-        
-        .. code-block:: bash
-        
-           $ cd docs
-           $ make html
-        
-        Doing so will only work if you have Sphinx installed, which you can acheive
-        through:
-        
-        .. code-block:: bash
-        
-           $ pip install -r requirements.txt
-        
-        Contributing
-        ============
-        
-        We welcome contributions to this library. However, there are a few ground
-        rules contributors should be aware of.
-        
-        License
-        -------
-        This project is licensed under the Apache v2.0 License. As such, your
-        contributions, once accepted, are automatically covered by this license.
-        
-        Commit messages
-        ---------------
-        Write decent commit messages. Don't use swear words and refrain from
-        uninformative commit messages as 'fixed typo'.
-        
-        The preferred format of a commit message:
-        
-        ::
-        
-            docs/quickstart: Fixed a typo in the Nodes section.
-        
-            If needed, elaborate further on this commit. Feel free to write a
-            complete blog post here if that helps us understand what this is
-            all about.
-        
-            Fixes #4 and resolves #2.
-        
-        If you'd like a more elaborate guide on how to write and format your commit
-        messages have a look at this post by `Tim Pope`_.
-        
-        .. _Tim Pope: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html
-        
-        Tests
-        -----
-        Commits are expected to contain tests or updates to tests if they add to or
-        modify the current behaviour.
-        
-        The test suite is powered by `pytest`_ and requires `pytest`_, `pytest-pep8`_,
-        `httpretty`_ and `pytest-httpretty`_ which will be installed for you if you
-        run:
-        
-        .. code-block:: bash
-        
-           $ pip install -r requirements.txt
-        
-        .. _pytest: http://pytest.org/latest/
-        .. _pytest-pep8: https://pypi.python.org/pypi/pytest-pep8
-        .. _httpretty: https://pypi.python.org/pypi/httpretty/
-        .. _pytest-httpretty: https://github.com/papaeye/pytest-httpretty
-        
-        To run the unit tests (the ones that don't require a live PuppetDB):
-        
-        .. code-block:: bash
-           
-           $ py.test -v -m unit
-        
-        If the tests pass, you're golden. If not we'll have to figure out why and
-        fix that. Feel free to ask for help on this.
-        
-        #########
-        Changelog
-        #########
-        
-        0.1.1
-        =====
-        
-        * Fix the license in our ``setup.py``. The license shouldn't be longer than
-          200 characters. We were including the full license tripping up tools like
-          bdist_rpm.
-        
-        0.1.0
-        =====
-        Significant changes have been made in this release. The complete v3 API is
-        now supported except for query pagination.
-        
-        Most changes are backwards compatible except for a change in the SSL
-        configuration. The previous behaviour was buggy and slightly misleading in
-        the names the options took:
-        
-        * ``ssl`` has been renamed to ``ssl_verify`` and now defaults to ``True``.
-        * Automatically use HTTPS if ``ssl_key`` and ``ssl_cert`` are provided.
-        
-        For additional instructions about getting SSL to work see the Quickstart
-        in the documentation.
-        
-        Deprecation
-        ------------
-        Support for API v2 will be dropped in the 0.2.x release series.
-        
-        New features
-        ------------
-        
-        The following features are **only** supported for **API v3**.
-        
-        The ``node()`` and ``nodes()`` function have gained the following options:
-        
-          * ``with_status=False``
-          * ``unreported=2``
-        
-        When ``with_status`` is set to ``True`` an additional query will be made using
-        the ``events-count`` endpoint scoped to the latest report. This will result in
-        an additional ``events`` and ``status`` keys on the node object. ``status``
-        will be either of ``changed``, ``unchanged`` or ``failed`` depending on if
-        ``events`` contains ``successes`` or ``failures`` or none.
-        
-        By default ``unreported`` is set to ``2``. This is only in effect when
-        ``with_status`` is set to ``True``. It means that if a node hasn't checked in
-        for two hours it will get a ``status`` of ``unreported`` instead.
-        
-        New endpoints:
-        
-          * ``events-count``: ``events_count()``
-          * ``aggregate-event-counts``: ``aggregate_event_counts()``
-          * ``server-time``: ``server_time()``
-          * ``version``: ``current_version()``
-          * ``catalog``: ``catalog()``
-        
-        New types:
-        
-          * ``pypuppetdb.types.Catalog``
-          * ``pypuppetdb.types.Edge``
-        
-        Changes to types:
-        
-          * ``pypuppetdb.types.Node`` now has:
-            * ``status`` defaulting to ``None``
-            * ``events`` defaulting to ``None``
-            * ``unreported_time`` defaulting to ``None``
-        
-        0.0.4
-        =====
-        
-        Due to a fairly serious bug 0.0.3 was pulled from PyPi minutes after release.
-        
-        When a bug was fixed to be able to query for all facts we accidentally
-        introduced a different bug that caused the ``facts()`` call on a node to
-        query for all facts because we were resetting the query.
-        
-        * Fix a bug where ``node.facts()`` was causing us to query all facts because
-          the query to scope our request was being reset.
-        
-        0.0.3
-        =====
-        
-        With the introduction of PuppetDB 1.5 a new API version, v3, was also
-        introduced. In that same release the old ``/experimental`` endpoints
-        were removed, meaning that as of PuppetDB 1.5 with the v2 API you can
-        no longer get access to reports or events.
-        
-        In light of this the support for the experimental endpoints has been
-        completely removed from pypuppetdb. As of this release you can only get
-        to reports and/or events through v3 of the API.
-        
-        This release includes preliminary support for the v3 API. Everything that
-        could be done with v2 plus the experimental endpoints is now possible on
-        v3. However, more advanced funtionality has not yet been implemented. That
-        will be the focus of the next release.
-        
-        * Removed dependency on pytz.
-        * Fixed the behaviour of ``facts()`` and ``resources()``. We can now
-          correctly query for all facts or resources.
-        * Fixed an issue with catalog timestampless nodes.
-        * Pass along the ``timeout`` option to ``connect()``.
-        * Added preliminary PuppetDB API v3 support.
-        * Removed support for the experimental endpoints.
-        * The ``connect()`` method defaults to API v3 now.
-        
-        0.0.2
-        =====
-        * Fix a bug in ``setup.py`` preventing successful installation.
-        
-        0.0.1
-        =====
-        Initial release. Implements most of the v2 API.
-        
-Keywords: puppet puppetdb
-Platform: UNKNOWN
-Classifier: Development Status :: 4 - Beta
-Classifier: Intended Audience :: Developers
-Classifier: Natural Language :: English
-Classifier: License :: OSI Approved :: Apache Software License
-Classifier: Operating System :: POSIX
-Classifier: Programming Language :: Python :: 2
-Classifier: Programming Language :: Python :: 2.6
-Classifier: Programming Language :: Python :: 2.7
-Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.3
-Classifier: Topic :: Software Development :: Libraries
diff --git a/README.rst b/README.rst
index c20d243..7d652ef 100644
--- a/README.rst
+++ b/README.rst
@@ -49,6 +49,35 @@ If you wish to hack on it clone the repository but after that run:
 This will install all the runtime requirements of pypuppetdb and the
 dependencies for the test suite and generation of documentation.
 
+Packages
+--------
+Native packages for your operating system will be provided in the near future.
+
++------------------+-----------+--------------------------------------------+
+| OS               | Status    |                                            |
++==================+===========+============================================+
+| Debian 6/Squeeze | planned   | Requires Backports                         |
++------------------+-----------+--------------------------------------------+
+| Debian 7/Wheezy  | planned   |                                            |
++------------------+-----------+--------------------------------------------+
+| Ubuntu 13.04     | planned   |                                            |
++------------------+-----------+--------------------------------------------+
+| Ubuntu 13.10     | planned   |                                            |
++------------------+-----------+--------------------------------------------+
+| CentOS/RHEL 5    | n/a       | Python 2.4                                 |
++------------------+-----------+--------------------------------------------+
+| CentOS/RHEL 6    | planned   |                                            |
++------------------+-----------+--------------------------------------------+
+| `ArchLinux`_     | available | Maintained by `Niels Abspoel`_             |
++------------------+-----------+--------------------------------------------+
+| `OpenBSD`_       | available | Maintained by `Jasper Lievisse Adriaanse`_ |
++------------------+-----------+--------------------------------------------+
+
+.. _ArchLinux: https://aur.archlinux.org/packages/python2-pypuppetdb/ 
+.. _Niels Abspoel: https://github.com/aboe76
+.. _Jasper Lievisse Adriaanse: https://github.com/jasperla
+.. _OpenBSD: http://www.openbsd.org/cgi-bin/cvsweb/ports/databases/py-puppetdb/
+
 Usage
 =====
 
@@ -138,6 +167,20 @@ Catalogs
 Will return a Catalog object with the latest Catalog of the definded host. This
 catalog contains the defined Resources and Edges.
 
+.. code-block:: python
+
+   >>> catalog = db.catalog('hostname')
+   >>> resource = catalog.get_resource('Service','ntp')
+   >>> for rel in resource.relationships:
+   >>>     print(rel)
+   Class[Ntp] - contains - Service[ntp]
+   File[/etc/ntp.conf] - notifies - Service[ntp]
+   File[/etc/ntp.conf] - required-by - Service[ntp]
+
+
+Will return all Relationships of a given Resource defined by type and title.
+This will list all linked other Resources and the type of relationship.
+
 Getting Help
 ============
 This project is still very new so it's not inconceivable you'll run into
@@ -190,6 +233,8 @@ License
 This project is licensed under the Apache v2.0 License. As such, your
 contributions, once accepted, are automatically covered by this license.
 
+Copyright (c) 2013-2014 Daniele Sluijters
+
 Commit messages
 ---------------
 Write decent commit messages. Don't use swear words and refrain from
diff --git a/conftest.py b/conftest.py
new file mode 100644
index 0000000..d636314
--- /dev/null
+++ b/conftest.py
@@ -0,0 +1,26 @@
+import pytest
+import pypuppetdb
+
+
+# Set up our API objects
+ at pytest.fixture(scope='session')
+def api2():
+    """Set up a connection to PuppetDB with API version 2."""
+    return pypuppetdb.connect(api_version=2)
+
+
+ at pytest.fixture(scope='session')
+def api3():
+    """Set up a connection to PuppetDB with API version 3."""
+    return pypuppetdb.connect(api_version=3)
+
+
+ at pytest.fixture
+def baseapi():
+    return pypuppetdb.api.BaseAPI(3)
+
+
+ at pytest.fixture
+def utc():
+    """Create a UTC object."""
+    return pypuppetdb.utils.UTC()
diff --git a/docs/Makefile b/docs/Makefile
new file mode 100644
index 0000000..168b975
--- /dev/null
+++ b/docs/Makefile
@@ -0,0 +1,153 @@
+# Makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS    =
+SPHINXBUILD   = sphinx-build
+PAPER         =
+BUILDDIR      = _build
+
+# Internal variables.
+PAPEROPT_a4     = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+ALLSPHINXOPTS   = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+# the i18n builder cannot share the environment and doctrees with the others
+I18NSPHINXOPTS  = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+
+.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
+
+help:
+	@echo "Please use \`make <target>' where <target> is one of"
+	@echo "  html       to make standalone HTML files"
+	@echo "  dirhtml    to make HTML files named index.html in directories"
+	@echo "  singlehtml to make a single large HTML file"
+	@echo "  pickle     to make pickle files"
+	@echo "  json       to make JSON files"
+	@echo "  htmlhelp   to make HTML files and a HTML help project"
+	@echo "  qthelp     to make HTML files and a qthelp project"
+	@echo "  devhelp    to make HTML files and a Devhelp project"
+	@echo "  epub       to make an epub"
+	@echo "  latex      to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
+	@echo "  latexpdf   to make LaTeX files and run them through pdflatex"
+	@echo "  text       to make text files"
+	@echo "  man        to make manual pages"
+	@echo "  texinfo    to make Texinfo files"
+	@echo "  info       to make Texinfo files and run them through makeinfo"
+	@echo "  gettext    to make PO message catalogs"
+	@echo "  changes    to make an overview of all changed/added/deprecated items"
+	@echo "  linkcheck  to check all external links for integrity"
+	@echo "  doctest    to run all doctests embedded in the documentation (if enabled)"
+
+clean:
+	-rm -rf $(BUILDDIR)/*
+
+html:
+	$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
+	@echo
+	@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
+
+dirhtml:
+	$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
+	@echo
+	@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
+
+singlehtml:
+	$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
+	@echo
+	@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
+
+pickle:
+	$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
+	@echo
+	@echo "Build finished; now you can process the pickle files."
+
+json:
+	$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
+	@echo
+	@echo "Build finished; now you can process the JSON files."
+
+htmlhelp:
+	$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
+	@echo
+	@echo "Build finished; now you can run HTML Help Workshop with the" \
+	      ".hhp project file in $(BUILDDIR)/htmlhelp."
+
+qthelp:
+	$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
+	@echo
+	@echo "Build finished; now you can run "qcollectiongenerator" with the" \
+	      ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
+	@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/PyPuppetDB.qhcp"
+	@echo "To view the help file:"
+	@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/PyPuppetDB.qhc"
+
+devhelp:
+	$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
+	@echo
+	@echo "Build finished."
+	@echo "To view the help file:"
+	@echo "# mkdir -p $$HOME/.local/share/devhelp/PyPuppetDB"
+	@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/PyPuppetDB"
+	@echo "# devhelp"
+
+epub:
+	$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
+	@echo
+	@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
+
+latex:
+	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+	@echo
+	@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
+	@echo "Run \`make' in that directory to run these through (pdf)latex" \
+	      "(use \`make latexpdf' here to do that automatically)."
+
+latexpdf:
+	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+	@echo "Running LaTeX files through pdflatex..."
+	$(MAKE) -C $(BUILDDIR)/latex all-pdf
+	@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+text:
+	$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
+	@echo
+	@echo "Build finished. The text files are in $(BUILDDIR)/text."
+
+man:
+	$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
+	@echo
+	@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
+
+texinfo:
+	$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+	@echo
+	@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
+	@echo "Run \`make' in that directory to run these through makeinfo" \
+	      "(use \`make info' here to do that automatically)."
+
+info:
+	$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+	@echo "Running Texinfo files through makeinfo..."
+	make -C $(BUILDDIR)/texinfo info
+	@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
+
+gettext:
+	$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
+	@echo
+	@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
+
+changes:
+	$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
+	@echo
+	@echo "The overview file is in $(BUILDDIR)/changes."
+
+linkcheck:
+	$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
+	@echo
+	@echo "Link check complete; look for any errors in the above output " \
+	      "or in $(BUILDDIR)/linkcheck/output.txt."
+
+doctest:
+	$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
+	@echo "Testing of doctests in the sources finished, look at the " \
+	      "results in $(BUILDDIR)/doctest/output.txt."
diff --git a/docs/api.rst b/docs/api.rst
new file mode 100644
index 0000000..d471d76
--- /dev/null
+++ b/docs/api.rst
@@ -0,0 +1,128 @@
+.. _api:
+
+Developer Interface
+===================
+
+.. module:: pypuppetdb
+
+This part of the documentation covers all the interfaces of PyPuppetDB.
+It will cover how the API is set up and how to configure which version of
+the API to use.
+
+Lazy objects
+------------
+
+.. note::
+
+   Reading in the response from PuppetDB is currently greedy, it will read in
+   the complete response no matter the size. This will change once streaming
+   and pagination support are added to PuppetDB's endpoints.
+
+In order for pypuppetdb to be able to deal with big datasets those functions
+that are expected to return more than a single item are implemented as
+generators.
+
+This is usually the case for functions with a plural name like
+:func:`~pypuppetdb.api.v2.API.nodes` or :func:`~pypuppetdb.api.v2.API.facts`.
+
+Because of this we'll only query PuppetDB once you start iterating over the
+generator object. Until that time not a single request is fired at PuppetDB.
+
+Most singular functions are implemented by calling their plural counterpart
+and then iterating over the generator, immediately exhausting the generator
+and returning a single/the first object.
+
+Main Interface
+--------------
+
+What you'll usually need to do is use the :func:`connect` method to set up a
+connection with PuppetDB and indicate which version of the API you want to
+talk.
+.. autofunction:: connect
+
+API objects
+-----------
+
+The PuppetDB API is versioned. We currently have a v1, v2 and v3.
+
+In order to work with this structure PyPuppetDB consists of a :class:`BaseAPI
+<BaseAPI>` class that factors out identical code between different versions.
+
+Every version of the API has its own class which inherits from our
+:class:`BaseAPI <BaseAPI>`.
+
+.. data:: API_VERSIONS
+
+   :obj:`dict` of :obj:`int`::obj:`string` pairs representing the API version
+   and it's URL prefix.
+
+   We currently only handle API version 2 though it should be fairly easy to
+   support version 1 should we want to.
+
+BaseAPI
+^^^^^^^
+
+.. autoclass:: pypuppetdb.api.BaseAPI
+   :members:
+   :private-members:
+
+v2.API
+^^^^^^
+.. autoclass:: pypuppetdb.api.v2.API
+   :members:
+   :inherited-members:
+   :private-members:
+   :show-inheritance:
+
+v3.API
+^^^^^^
+.. autoclass:: pypuppetdb.api.v3.API
+   :members:
+   :inherited-members:
+   :private-members:
+   :show-inheritance:
+
+Types
+-----
+
+In order to facilitate working with the API most methods like
+:meth:`~pypuppetdb.api.v2.API.nodes` don't return the decoded
+JSON response but return an object representation of the querried
+endpoints data.
+
+.. autoclass:: pypuppetdb.types.Node
+   :members:
+.. autoclass:: pypuppetdb.types.Fact
+.. autoclass:: pypuppetdb.types.Resource
+.. autoclass:: pypuppetdb.types.Event
+.. autoclass:: pypuppetdb.types.Report
+.. autoclass:: pypuppetdb.types.Catalog
+.. autoclass:: pypuppetdb.types.Edge
+
+Errors
+------
+
+Unfortunately things can go haywire. PuppetDB might not be reachable
+or complain about our query, requests might have to wait too long to
+recieve a response or the body is just too big to handle.
+
+In that case, we'll throw an exception at you.
+
+.. autoexception:: pypuppetdb.errors.APIError
+.. autoexception:: pypuppetdb.errors.ImproperlyConfiguredError
+   :show-inheritance:
+.. autoexception:: pypuppetdb.errors.UnsupportedVersionError
+   :show-inheritance:
+.. autoexception:: pypuppetdb.errors.DoesNotComputeError
+   :show-inheritance:
+.. autoexception:: pypuppetdb.errors.EmptyResponseError
+   :show-inheritance:
+
+Utilities
+---------
+
+A few functions that are used across this library have been put
+into their own :mod:`utils` module.
+
+.. autoclass::    pypuppetdb.utils.UTC
+.. autofunction:: pypuppetdb.utils.json_to_datetime
diff --git a/docs/conf.py b/docs/conf.py
new file mode 100644
index 0000000..952db68
--- /dev/null
+++ b/docs/conf.py
@@ -0,0 +1,60 @@
+# -*- coding: utf-8 -*-
+import sys, os
+
+pypuppetdb_root = os.path.dirname(os.path.abspath('.'))
+sys.path.insert(0, pypuppetdb_root)
+import pypuppetdb.package
+
+# -- General configuration -----------------------------------------------------
+
+extensions = ['sphinx.ext.autodoc', 'sphinx.ext.viewcode']
+
+templates_path = ['_templates']
+
+source_suffix = '.rst'
+
+master_doc = 'index'
+
+project = pypuppetdb.package.__title__
+copyright = '{0}, {1}'.format(pypuppetdb.package.__year__,
+                              pypuppetdb.package.__author__)
+
+version = pypuppetdb.package.__version__
+release = version
+
+language = 'en'
+
+exclude_patterns = ['_build']
+
+pygments_style = 'sphinx'
+
+# -- Options for HTML output ---------------------------------------------------
+
+html_theme = 'default'
+
+html_static_path = ['_static']
+
+htmlhelp_basename = 'pypuppetdbdoc'
+
+# -- Options for LaTeX output --------------------------------------------------
+
+latex_documents = [
+  ('index', 'pypuppetdb.tex', u'pypuppetdb Documentation',
+   u'Daniele Sluijters', 'manual'),
+]
+
+
+# -- Options for manual page output --------------------------------------------
+
+man_pages = [
+    ('index', 'pypuppetdb', u'pypuppetdb Documentation',
+     [u'Daniele Sluijters'], 1)
+]
+
+# -- Options for Texinfo output ------------------------------------------------
+
+texinfo_documents = [
+  ('index', 'pypuppetdb', u'pypuppetdb Documentation',
+   u'Daniele Sluijters', 'pypuppetdb', 'Library to work with the PuppetDB REST API.',
+   'Miscellaneous'),
+]
diff --git a/docs/index.rst b/docs/index.rst
new file mode 100644
index 0000000..3060cfa
--- /dev/null
+++ b/docs/index.rst
@@ -0,0 +1,37 @@
+Welcome to pypuppetdb's documentation!
+======================================
+
+.. note::
+   This is a very new project and still changing at a rapid pace. As such the
+   only documentation currently available is the API documentation and a brief
+   Getting Started guide. Once this settles down tutorials and other documentation
+   will be added over time.
+
+Getting started
+---------------
+
+The quickstart should get you up and running with pypuppetdb and familiarise
+you with how this library works.
+
+.. toctree::
+   :maxdepth: 2
+
+   quickstart
+
+API Documentation
+-----------------
+
+This part of the documentation focusses on the classes, methods and functions
+that make up this library.
+
+.. toctree::
+   :maxdepth: 2
+
+   api
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`search`
+
diff --git a/docs/make.bat b/docs/make.bat
new file mode 100644
index 0000000..fe092a2
--- /dev/null
+++ b/docs/make.bat
@@ -0,0 +1,190 @@
+ at ECHO OFF
+
+REM Command file for Sphinx documentation
+
+if "%SPHINXBUILD%" == "" (
+	set SPHINXBUILD=sphinx-build
+)
+set BUILDDIR=_build
+set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
+set I18NSPHINXOPTS=%SPHINXOPTS% .
+if NOT "%PAPER%" == "" (
+	set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
+	set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
+)
+
+if "%1" == "" goto help
+
+if "%1" == "help" (
+	:help
+	echo.Please use `make ^<target^>` where ^<target^> is one of
+	echo.  html       to make standalone HTML files
+	echo.  dirhtml    to make HTML files named index.html in directories
+	echo.  singlehtml to make a single large HTML file
+	echo.  pickle     to make pickle files
+	echo.  json       to make JSON files
+	echo.  htmlhelp   to make HTML files and a HTML help project
+	echo.  qthelp     to make HTML files and a qthelp project
+	echo.  devhelp    to make HTML files and a Devhelp project
+	echo.  epub       to make an epub
+	echo.  latex      to make LaTeX files, you can set PAPER=a4 or PAPER=letter
+	echo.  text       to make text files
+	echo.  man        to make manual pages
+	echo.  texinfo    to make Texinfo files
+	echo.  gettext    to make PO message catalogs
+	echo.  changes    to make an overview over all changed/added/deprecated items
+	echo.  linkcheck  to check all external links for integrity
+	echo.  doctest    to run all doctests embedded in the documentation if enabled
+	goto end
+)
+
+if "%1" == "clean" (
+	for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
+	del /q /s %BUILDDIR%\*
+	goto end
+)
+
+if "%1" == "html" (
+	%SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished. The HTML pages are in %BUILDDIR%/html.
+	goto end
+)
+
+if "%1" == "dirhtml" (
+	%SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
+	goto end
+)
+
+if "%1" == "singlehtml" (
+	%SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
+	goto end
+)
+
+if "%1" == "pickle" (
+	%SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished; now you can process the pickle files.
+	goto end
+)
+
+if "%1" == "json" (
+	%SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished; now you can process the JSON files.
+	goto end
+)
+
+if "%1" == "htmlhelp" (
+	%SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished; now you can run HTML Help Workshop with the ^
+.hhp project file in %BUILDDIR%/htmlhelp.
+	goto end
+)
+
+if "%1" == "qthelp" (
+	%SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished; now you can run "qcollectiongenerator" with the ^
+.qhcp project file in %BUILDDIR%/qthelp, like this:
+	echo.^> qcollectiongenerator %BUILDDIR%\qthelp\PyPuppetDB.qhcp
+	echo.To view the help file:
+	echo.^> assistant -collectionFile %BUILDDIR%\qthelp\PyPuppetDB.ghc
+	goto end
+)
+
+if "%1" == "devhelp" (
+	%SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished.
+	goto end
+)
+
+if "%1" == "epub" (
+	%SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished. The epub file is in %BUILDDIR%/epub.
+	goto end
+)
+
+if "%1" == "latex" (
+	%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
+	goto end
+)
+
+if "%1" == "text" (
+	%SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished. The text files are in %BUILDDIR%/text.
+	goto end
+)
+
+if "%1" == "man" (
+	%SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished. The manual pages are in %BUILDDIR%/man.
+	goto end
+)
+
+if "%1" == "texinfo" (
+	%SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
+	goto end
+)
+
+if "%1" == "gettext" (
+	%SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
+	goto end
+)
+
+if "%1" == "changes" (
+	%SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.The overview file is in %BUILDDIR%/changes.
+	goto end
+)
+
+if "%1" == "linkcheck" (
+	%SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Link check complete; look for any errors in the above output ^
+or in %BUILDDIR%/linkcheck/output.txt.
+	goto end
+)
+
+if "%1" == "doctest" (
+	%SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Testing of doctests in the sources finished, look at the ^
+results in %BUILDDIR%/doctest/output.txt.
+	goto end
+)
+
+:end
diff --git a/docs/quickstart.rst b/docs/quickstart.rst
new file mode 100644
index 0000000..a38015d
--- /dev/null
+++ b/docs/quickstart.rst
@@ -0,0 +1,133 @@
+.. _quickstart:
+
+Quickstart
+==========
+Once you have pypuppetdb installed you can configure it to connect to PuppetDB
+and take it from there.
+
+Connecting
+----------
+
+The first thing you need to do is to connect with PuppetDB:
+
+.. code-block:: python
+
+   >>> from pypuppetdb import connect
+   >>> db = connect()
+
+Nodes
+-----
+
+The following will return a generator object yielding Node objects for every
+returned node from PuppetDB.
+
+.. code-block:: python
+
+   >>> nodes = db.nodes()
+   >>> for node in nodes:
+   >>>   print(node)
+   host1
+   host2
+   ...
+
+To query a single node the singular `node()` can be used:
+
+.. code-block:: python
+
+    >>> node = db.node('hostname')
+    >>> print(node)
+    hostname
+
+Node scope
+~~~~~~~~~~
+
+The Node objects are a bit more special in that they can query for facts and
+resources themselves. Using those methods from a node object will automatically
+add a query to the request scoping the request to the node.
+
+.. code-block:: python
+
+   >>> node = db.node('hostname')
+   >>> print(node.fact('osfamily'))
+   osfamily/hostname
+
+Facts
+-----
+
+.. code-block:: python
+
+   >>> facts = db.facts('osfamily')
+   >>> for fact in facts:
+   >>>   print(fact)
+   osfamily/host1
+   osfamily/host2
+
+That queries PuppetDB for the 'osfamily' fact and will yield Fact objects,
+one per node this fact is known for.
+
+Resources
+---------
+
+.. code-block:: python
+
+   >>> resources = db.resources('file')
+
+Will return a generator object containing all file resources you're managing
+across your infrastructure. This is probably a bad idea if you have a big
+number of nodes as the response will be huge.
+
+SSL
+---
+If PuppetDB and the tool that's using pypuppetdb aren't located on the same
+machine you will have to connect securely to PuppetDB using client certificates
+according to PuppetDB's default configuration.
+
+You can also tell PuppetDB to accept plain connections from anywhere instead
+of just the local machine but **don't do that**.
+
+Pypuppetdb can handle this easily for you. It requires two things:
+  * Generate with your Puppet CA a key pair that you want to use
+  * Tell pypuppetdb to use this keypair.
+
+Generate keypair
+~~~~~~~~~~~~~~~~
+
+On your Puppet Master or dedicated Puppet CA server:
+
+.. code-block:: console
+
+   $ puppet cert generate <service_name>
+
+Once that's done you'll need to get the public and private keyfile and copy
+them over. You can find those in Puppet's ``$ssldir``, usually
+``/var/lib/puppet/ssl``:
+
+  * private key: ``$ssldir/private_keys/<service_name>.pem``
+  * public key: ``$ssldir/ca/signed/<service_name>.pem``
+
+Configure pypuppetdb for SSL
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Once you have those you can pass them to pypuppetdb's ``connect()``:
+
+.. code-block:: python
+
+   >>> db = connect(ssl_key='/path/to/private.pem', ssl_cert='/path/to/public.pem')
+
+If both ``ssl_key`` and ``ssl_cert`` are provided pypuppetdb will automatically
+switch over to using HTTPS instead.
+
+By default pypuppetdb will also verify the certificate PuppetDB is serving.
+This means that the authority that signed PuppetDB's server certificate, most
+likely your Puppet Master, must be part of the trusted set of certificates for
+your OS or must be added to that set. Those certificates are usually found in
+``/etc/ssl/certs`` on Linux-y machines.
+
+For Debian, install your Puppet Master's certificate in
+``/usr/local/share/ca-certifiactes`` with a ``.crt`` extension and then run
+``dpkg-reconfigure ca-certificates`` as per
+``/usr/share/doc/ca-certificates/README.Debian``. This of course requires the
+``ca-certificates`` package to be installed.
+
+If you do not wish to do so or for whatever reason want to disable the
+verification of PuppetDB's certificate you can pass in ``ssl_verify=False``.
diff --git a/pypuppetdb.egg-info/PKG-INFO b/pypuppetdb.egg-info/PKG-INFO
deleted file mode 100644
index 4e78d5b..0000000
--- a/pypuppetdb.egg-info/PKG-INFO
+++ /dev/null
@@ -1,378 +0,0 @@
-Metadata-Version: 1.1
-Name: pypuppetdb
-Version: 0.1.1
-Summary: Library for working with the PuppetDB REST API.
-Home-page: https://github.com/nedap/pypuppetdb
-Author: Daniele Sluijters
-Author-email: daniele.sluijters+pypi at gmail.com
-License: Apache License 2.0
-Description: ##########
-        pypuppetdb
-        ##########
-        
-        .. image:: https://api.travis-ci.org/nedap/pypuppetdb.png
-           :target: https://travis-ci.org/nedap/pypuppetdb
-        
-        .. image:: https://coveralls.io/repos/nedap/pypuppetdb/badge.png
-           :target: https://coeralls.io/r/nedap/pypuppetdb
-        
-        .. image:: https://pypip.in/d/pypuppetdb/badge.png
-           :target: https://crate.io/packages/pypuppetdb
-        
-        .. image:: https://pypip.in/v/pypuppetdb/badge.png
-           :target: https://crate.io/packages/pypuppetdb
-        
-        pypuppetdtb is a library to work with PuppetDB's REST API. It is implemented
-        using the `requests`_ library.
-        
-        .. _requests: http://docs.python-requests.org/en/latest/
-        
-        This library is a thin wrapper around the REST API providing some convinience
-        functions and objects to request and hold data from PuppetDB.
-        
-        To use this library you will need:
-            * Python 2.6 or 2.7
-            * Python 3.3
-        
-        Installation
-        ============
-        
-        You can install this package from source or from PyPi.
-        
-        .. code-block:: bash
-        
-           $ pip install pypuppetdb
-        
-        .. code-block:: bash
-        
-           $ git clone https://github.com/nedap/pypuppetdb
-           $ python setup.py install
-        
-        If you wish to hack on it clone the repository but after that run:
-        
-        .. code-block:: bash
-        
-           $ pip install -r requirements.txt
-        
-        This will install all the runtime requirements of pypuppetdb and the
-        dependencies for the test suite and generation of documentation.
-        
-        Usage
-        =====
-        
-        Once you have pypuppetdb installed you can configure it to connect to PuppetDB
-        and take it from there.
-        
-        Connecting
-        ----------
-        
-        The first thing you need to do is to connect with PuppetDB:
-        
-        .. code-block:: python
-        
-           >>> from pypuppetdb import connect
-           >>> db = connect()
-        
-        Nodes
-        -----
-        
-        The following will return a generator object yielding Node objects for every
-        returned node from PuppetDB.
-        
-        .. code-block:: python
-        
-           >>> nodes = db.nodes()
-           >>> for node in nodes:
-           >>>   print(node)
-           host1
-           host2
-           ...
-        
-        To query a single node the singular `node()` can be used:
-        
-        .. code-block:: python
-        
-            >>> node = db.node('hostname')
-            >>> print(node)
-            hostname
-        
-        Node scope
-        ~~~~~~~~~~
-        
-        The Node objects are a bit more special in that they can query for facts and
-        resources themselves. Using those methods from a node object will automatically
-        add a query to the request scoping the request to the node.
-        
-        .. code-block:: python
-        
-           >>> node = db.node('hostname')
-           >>> print(node.fact('osfamily'))
-           osfamily/hostname
-        
-        Facts
-        -----
-        
-        .. code-block:: python
-        
-           >>> facts = db.facts('osfamily')
-           >>> for fact in facts:
-           >>>   print(fact)
-           osfamily/host1
-           osfamily/host2
-        
-        That queries PuppetDB for the 'osfamily' fact and will yield Fact objects,
-        one per node this fact is known for.
-        
-        Resources
-        ---------
-        
-        .. code-block:: python
-        
-           >>> resources = db.resources('file')
-        
-        Will return a generator object containing all file resources you're managing
-        across your infrastructure. This is probably a bad idea if you have a big
-        number of nodes as the response will be huge.
-        
-        Catalogs
-        ---------
-        
-        .. code-block:: python
-        
-           >>> catalog = db.catalog('hostname')
-           >>> for res in catalog.get_resources():
-           >>>     print(res)
-        
-        Will return a Catalog object with the latest Catalog of the definded host. This
-        catalog contains the defined Resources and Edges.
-        
-        Getting Help
-        ============
-        This project is still very new so it's not inconceivable you'll run into
-        issues.
-        
-        For bug reports you can file an `issue`_. If you need help with something
-        feel free to hit up `@daenney`_ by e-mail or find him on IRC. He can usually
-        be found on `IRCnet`_ and `Freenode`_ and idles in #puppet.
-        
-        There's now also the #puppetboard channel on `Freenode`_ where we hang out
-        and answer questions related to pypuppetdb and Puppetboard.
-        
-        .. _issue: https://github.com/nedap/pypuppetdb/issues
-        .. _ at daenney: https://github.com/daenney
-        .. _IRCnet: http://www.ircnet.org
-        .. _Freenode: http://freenode.net
-        
-        Documentation
-        =============
-        API documentation is automatically generated from the docstrings using
-        Sphinx's autodoc feature. 
-        
-        Documentation will automatically be rebuilt on every push thanks to the
-        Read The Docs webhook. You can `find it here`_.
-        
-        .. _find it here: https://pypuppetdb.readthedocs.org/en/latest/
-        
-        You can build the documentation manually by doing:
-        
-        .. code-block:: bash
-        
-           $ cd docs
-           $ make html
-        
-        Doing so will only work if you have Sphinx installed, which you can acheive
-        through:
-        
-        .. code-block:: bash
-        
-           $ pip install -r requirements.txt
-        
-        Contributing
-        ============
-        
-        We welcome contributions to this library. However, there are a few ground
-        rules contributors should be aware of.
-        
-        License
-        -------
-        This project is licensed under the Apache v2.0 License. As such, your
-        contributions, once accepted, are automatically covered by this license.
-        
-        Commit messages
-        ---------------
-        Write decent commit messages. Don't use swear words and refrain from
-        uninformative commit messages as 'fixed typo'.
-        
-        The preferred format of a commit message:
-        
-        ::
-        
-            docs/quickstart: Fixed a typo in the Nodes section.
-        
-            If needed, elaborate further on this commit. Feel free to write a
-            complete blog post here if that helps us understand what this is
-            all about.
-        
-            Fixes #4 and resolves #2.
-        
-        If you'd like a more elaborate guide on how to write and format your commit
-        messages have a look at this post by `Tim Pope`_.
-        
-        .. _Tim Pope: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html
-        
-        Tests
-        -----
-        Commits are expected to contain tests or updates to tests if they add to or
-        modify the current behaviour.
-        
-        The test suite is powered by `pytest`_ and requires `pytest`_, `pytest-pep8`_,
-        `httpretty`_ and `pytest-httpretty`_ which will be installed for you if you
-        run:
-        
-        .. code-block:: bash
-        
-           $ pip install -r requirements.txt
-        
-        .. _pytest: http://pytest.org/latest/
-        .. _pytest-pep8: https://pypi.python.org/pypi/pytest-pep8
-        .. _httpretty: https://pypi.python.org/pypi/httpretty/
-        .. _pytest-httpretty: https://github.com/papaeye/pytest-httpretty
-        
-        To run the unit tests (the ones that don't require a live PuppetDB):
-        
-        .. code-block:: bash
-           
-           $ py.test -v -m unit
-        
-        If the tests pass, you're golden. If not we'll have to figure out why and
-        fix that. Feel free to ask for help on this.
-        
-        #########
-        Changelog
-        #########
-        
-        0.1.1
-        =====
-        
-        * Fix the license in our ``setup.py``. The license shouldn't be longer than
-          200 characters. We were including the full license tripping up tools like
-          bdist_rpm.
-        
-        0.1.0
-        =====
-        Significant changes have been made in this release. The complete v3 API is
-        now supported except for query pagination.
-        
-        Most changes are backwards compatible except for a change in the SSL
-        configuration. The previous behaviour was buggy and slightly misleading in
-        the names the options took:
-        
-        * ``ssl`` has been renamed to ``ssl_verify`` and now defaults to ``True``.
-        * Automatically use HTTPS if ``ssl_key`` and ``ssl_cert`` are provided.
-        
-        For additional instructions about getting SSL to work see the Quickstart
-        in the documentation.
-        
-        Deprecation
-        ------------
-        Support for API v2 will be dropped in the 0.2.x release series.
-        
-        New features
-        ------------
-        
-        The following features are **only** supported for **API v3**.
-        
-        The ``node()`` and ``nodes()`` function have gained the following options:
-        
-          * ``with_status=False``
-          * ``unreported=2``
-        
-        When ``with_status`` is set to ``True`` an additional query will be made using
-        the ``events-count`` endpoint scoped to the latest report. This will result in
-        an additional ``events`` and ``status`` keys on the node object. ``status``
-        will be either of ``changed``, ``unchanged`` or ``failed`` depending on if
-        ``events`` contains ``successes`` or ``failures`` or none.
-        
-        By default ``unreported`` is set to ``2``. This is only in effect when
-        ``with_status`` is set to ``True``. It means that if a node hasn't checked in
-        for two hours it will get a ``status`` of ``unreported`` instead.
-        
-        New endpoints:
-        
-          * ``events-count``: ``events_count()``
-          * ``aggregate-event-counts``: ``aggregate_event_counts()``
-          * ``server-time``: ``server_time()``
-          * ``version``: ``current_version()``
-          * ``catalog``: ``catalog()``
-        
-        New types:
-        
-          * ``pypuppetdb.types.Catalog``
-          * ``pypuppetdb.types.Edge``
-        
-        Changes to types:
-        
-          * ``pypuppetdb.types.Node`` now has:
-            * ``status`` defaulting to ``None``
-            * ``events`` defaulting to ``None``
-            * ``unreported_time`` defaulting to ``None``
-        
-        0.0.4
-        =====
-        
-        Due to a fairly serious bug 0.0.3 was pulled from PyPi minutes after release.
-        
-        When a bug was fixed to be able to query for all facts we accidentally
-        introduced a different bug that caused the ``facts()`` call on a node to
-        query for all facts because we were resetting the query.
-        
-        * Fix a bug where ``node.facts()`` was causing us to query all facts because
-          the query to scope our request was being reset.
-        
-        0.0.3
-        =====
-        
-        With the introduction of PuppetDB 1.5 a new API version, v3, was also
-        introduced. In that same release the old ``/experimental`` endpoints
-        were removed, meaning that as of PuppetDB 1.5 with the v2 API you can
-        no longer get access to reports or events.
-        
-        In light of this the support for the experimental endpoints has been
-        completely removed from pypuppetdb. As of this release you can only get
-        to reports and/or events through v3 of the API.
-        
-        This release includes preliminary support for the v3 API. Everything that
-        could be done with v2 plus the experimental endpoints is now possible on
-        v3. However, more advanced funtionality has not yet been implemented. That
-        will be the focus of the next release.
-        
-        * Removed dependency on pytz.
-        * Fixed the behaviour of ``facts()`` and ``resources()``. We can now
-          correctly query for all facts or resources.
-        * Fixed an issue with catalog timestampless nodes.
-        * Pass along the ``timeout`` option to ``connect()``.
-        * Added preliminary PuppetDB API v3 support.
-        * Removed support for the experimental endpoints.
-        * The ``connect()`` method defaults to API v3 now.
-        
-        0.0.2
-        =====
-        * Fix a bug in ``setup.py`` preventing successful installation.
-        
-        0.0.1
-        =====
-        Initial release. Implements most of the v2 API.
-        
-Keywords: puppet puppetdb
-Platform: UNKNOWN
-Classifier: Development Status :: 4 - Beta
-Classifier: Intended Audience :: Developers
-Classifier: Natural Language :: English
-Classifier: License :: OSI Approved :: Apache Software License
-Classifier: Operating System :: POSIX
-Classifier: Programming Language :: Python :: 2
-Classifier: Programming Language :: Python :: 2.6
-Classifier: Programming Language :: Python :: 2.7
-Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.3
-Classifier: Topic :: Software Development :: Libraries
diff --git a/pypuppetdb.egg-info/SOURCES.txt b/pypuppetdb.egg-info/SOURCES.txt
deleted file mode 100644
index 54737cd..0000000
--- a/pypuppetdb.egg-info/SOURCES.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-CHANGELOG.rst
-LICENSE
-MANIFEST.in
-README.rst
-setup.cfg
-setup.py
-pypuppetdb/__init__.py
-pypuppetdb/errors.py
-pypuppetdb/package.py
-pypuppetdb/types.py
-pypuppetdb/utils.py
-pypuppetdb.egg-info/PKG-INFO
-pypuppetdb.egg-info/SOURCES.txt
-pypuppetdb.egg-info/dependency_links.txt
-pypuppetdb.egg-info/requires.txt
-pypuppetdb.egg-info/top_level.txt
-pypuppetdb/api/__init__.py
-pypuppetdb/api/v2.py
-pypuppetdb/api/v3.py
\ No newline at end of file
diff --git a/pypuppetdb.egg-info/dependency_links.txt b/pypuppetdb.egg-info/dependency_links.txt
deleted file mode 100644
index 8b13789..0000000
--- a/pypuppetdb.egg-info/dependency_links.txt
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/pypuppetdb.egg-info/requires.txt b/pypuppetdb.egg-info/requires.txt
deleted file mode 100644
index ce40e87..0000000
--- a/pypuppetdb.egg-info/requires.txt
+++ /dev/null
@@ -1 +0,0 @@
-requests >= 1.2.3
\ No newline at end of file
diff --git a/pypuppetdb.egg-info/top_level.txt b/pypuppetdb.egg-info/top_level.txt
deleted file mode 100644
index 88c4c96..0000000
--- a/pypuppetdb.egg-info/top_level.txt
+++ /dev/null
@@ -1 +0,0 @@
-pypuppetdb
diff --git a/pypuppetdb/api/__init__.py b/pypuppetdb/api/__init__.py
index a77178a..201069e 100644
--- a/pypuppetdb/api/__init__.py
+++ b/pypuppetdb/api/__init__.py
@@ -106,6 +106,12 @@ class BaseAPI(object):
         self.ssl_cert = ssl_cert
         self.timeout = timeout
         self.endpoints = ENDPOINTS[api_version]
+        self._session = requests.Session()
+        self._session.headers = {
+            'content-type': 'application/json',
+            'accept': 'application/json',
+            'accept-charset': 'utf-8'
+        }
 
         if self.ssl_key is not None and self.ssl_cert is not None:
             self.protocol = 'https'
@@ -145,6 +151,16 @@ class BaseAPI(object):
         if self.last_total is not None:
             return int(self.last_total)
 
+    def _normalize_resource_type(self, type_):
+        """Normalizes the type passed to the api by capitalizing each part
+        of the type. For example:
+
+        sysctl::value -> Sysctl::Value
+        user -> User
+
+        """
+        return '::'.join([s.capitalize() for s in type_.split('::')])
+
     def _url(self, endpoint, path=None):
         """The complete URL we will end up querying. Depending on the
         endpoint we pass in  this will result in different URL's with
@@ -232,11 +248,6 @@ class BaseAPI(object):
                                              count_filter))
 
         url = self._url(endpoint, path=path)
-        headers = {
-            'content-type': 'application/json',
-            'accept': 'application/json',
-            'accept-charset': 'utf-8'
-            }
 
         payload = {}
         if query is not None:
@@ -260,10 +271,9 @@ class BaseAPI(object):
             payload = None
 
         try:
-            r = requests.get(url, params=payload, headers=headers,
-                             verify=self.ssl_verify, cert=(self.ssl_cert,
-                                                           self.ssl_key),
-                             timeout=self.timeout)
+            r = self._session.get(url, params=payload, verify=self.ssl_verify,
+                                  cert=(self.ssl_cert, self.ssl_key),
+                                  timeout=self.timeout)
             r.raise_for_status()
 
             # get total number of results if requested with include-total
diff --git a/pypuppetdb/api/v2.py b/pypuppetdb/api/v2.py
index 185c23a..2d415d7 100644
--- a/pypuppetdb/api/v2.py
+++ b/pypuppetdb/api/v2.py
@@ -93,10 +93,8 @@ class API(BaseAPI):
         This will yield a Resources object for every returned resource."""
 
         if type_ is not None:
-            # Need to capitalize the resource type since PuppetDB doesn't
-            # answer to lower case type names.
-            # bugs.puppetlabs.com/some_value
-            type_ = type_.capitalize()
+            type_ = self._normalize_resource_type(type_)
+
             if title is not None:
                 path = '{0}/{1}'.format(type_, title)
             elif title is None:
diff --git a/pypuppetdb/api/v3.py b/pypuppetdb/api/v3.py
index f5a8705..090cae4 100644
--- a/pypuppetdb/api/v3.py
+++ b/pypuppetdb/api/v3.py
@@ -78,6 +78,8 @@ class API(BaseAPI):
                 node['events'] = status = status[0]
                 if status['successes'] > 0:
                     node['status'] = 'changed'
+                if status['noops'] > 0:
+                    node['status'] = 'noop'
                 if status['failures'] > 0:
                     node['status'] = 'failed'
             else:
@@ -150,22 +152,19 @@ class API(BaseAPI):
         """Query for resources limited by either type and/or title or query.
         This will yield a Resources object for every returned resource."""
 
-        log.debug('YOLO')
+        path = None
+
         if type_ is not None:
-            # Need to capitalize the resource type since PuppetDB doesn't
-            # answer to lower case type names.
-            # bugs.puppetlabs.com/some_value
-            type_ = type_.capitalize()
+            type_ = self._normalize_resource_type(type_)
+
             if title is not None:
                 path = '{0}/{1}'.format(type_, title)
             elif title is None:
                 path = type_
-        else:
+        elif query is None:
             log.debug('Going to query for all resources. This is usually a '
                       'bad idea as it might return enormous amounts of '
                       'resources.')
-            query = ''
-            path = None
 
         resources = self._query('resources', path=path, query=query)
         for resource in resources:
@@ -200,12 +199,13 @@ class API(BaseAPI):
                 report['transaction-uuid']
                 )
 
-    def events(self, query):
+    def events(self, query, order_by=None, limit=None):
         """A report is made up of events. This allows to query for events
         based on the reprt hash.
         This yields an Event object for every returned event."""
 
-        events = self._query('events', query=query)
+        events = self._query('events', query=query,
+                             order_by=order_by, limit=limit)
         for event in events:
             yield Event(
                 event['certname'],
diff --git a/pypuppetdb/types.py b/pypuppetdb/types.py
index 1a1af94..ad96e34 100644
--- a/pypuppetdb/types.py
+++ b/pypuppetdb/types.py
@@ -168,6 +168,8 @@ class Resource(object):
     :ivar sourcefile: The Puppet manifest this resource is declared in.
     :ivar sourceline: The line this resource is declared at.
     :ivar parameters: :obj:`dict` with key:value pairs of parameters.
+    :ivar relationships: :obj:`list` Contains all relationships to other\
+        resources
     """
     def __init__(self, node, name, type_, tags, exported, sourcefile,
                  sourceline, parameters={}):
@@ -179,6 +181,7 @@ class Resource(object):
         self.sourcefile = sourcefile
         self.sourceline = sourceline
         self.parameters = parameters
+        self.relationships = []
         self.__string = '{0}[{1}]'.format(self.type_, self.name)
 
     def __repr__(self):
@@ -353,9 +356,12 @@ class Catalog(object):
                 '[' + edge['source']['title'] + ']'
             identifier_target = edge['target']['type'] + \
                 '[' + edge['target']['title'] + ']'
-            self.edges.append(Edge(self.resources[identifier_source],
-                              self.resources[identifier_target],
-                              edge['relationship']))
+            e = Edge(self.resources[identifier_source],
+                     self.resources[identifier_target],
+                     edge['relationship'])
+            self.edges.append(e)
+            self.resources[identifier_source].relationships.append(e)
+            self.resources[identifier_target].relationships.append(e)
 
         self.__string = '{0}/{1}'.format(self.node, self.transaction_uuid)
 
@@ -371,6 +377,11 @@ class Catalog(object):
     def get_resources(self):
         return self.resources.itervalues()
 
+    def get_resource(self, resource_type, resource_title):
+        identifier = resource_type + \
+            '[' + resource_title + ']'
+        return self.resources[identifier]
+
     def get_edges(self):
         return iter(self.edges)
 
diff --git a/python-pypuppetdb.spec b/python-pypuppetdb.spec
new file mode 100644
index 0000000..eeda8ea
--- /dev/null
+++ b/python-pypuppetdb.spec
@@ -0,0 +1,48 @@
+%{!?python_sitelib: %global python_sitelib %(%{__python} -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")}
+
+Name:           python-pypuppetdb
+Version:        0.0.4
+Release:        2%{?dist}
+Summary:        A Python puppetdb API
+
+Group:          Development/Languages
+License:        Apache
+URL:            https://github.com/nedap/pypuppetdb
+Source0:        http://pypi.python.org/packages/source/p/pypuppetdb/pypuppetdb-%{version}.tar.gz
+BuildRoot:      %{_tmppath}/%{name}-%{version}-%{release}
+
+BuildArch:      noarch
+BuildRequires:  python-setuptools
+Requires: python-requests >= 1.1.0
+Requires: python >= 2.6
+
+%description
+pypuppetdb is a library to work with PuppetDB's REST API. It is implemented using the requests library.
+This library is a thin wrapper around the REST API providing some convinience functions and objects to request and hold data from PuppetDB.
+To use this library you will need: Python 2.6 or 2.7 or Python 3.3.
+
+%prep
+%setup -q -n pypuppetdb-%{version}
+%{__rm} -rf *.egg-info
+%{__sed} -i 's,^#!.*env python.*$,#!/usr/bin/python,' setup.py
+
+%build
+
+%install
+rm -rf %{buildroot}
+%{__python} setup.py install -O1 --root %{buildroot}
+
+%clean
+rm -rf %{buildroot}
+
+%files
+%defattr(-,root,root,-)
+%doc
+%{python_sitelib}/*
+
+%changelog
+* Tue Oct 15 2013 Klavs Klavsen <Klavs at EnableIT.dk> - 0.0.4-2
+- Add requirements, description and other small cleanups.
+* Mon Oct 14 2013 Klavs Klavsen <Klavs at EnableIT.dk> - 0.0.4-1
+- Initial release.
+
diff --git a/setup.cfg b/setup.cfg
index 3cfb4d5..9581b3a 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -2,14 +2,8 @@
 norecursedirs = docs .tox
 minversion = 2.3
 markers = 
-	unit: mark test as a unit test. Runs without PuppetDB.
-	integration: mark test as an integration test. Needs a live PuppetDB with testdata loaded.
+    unit: mark test as a unit test. Runs without PuppetDB.
+    integration: mark test as an integration test. Needs a live PuppetDB with testdata loaded.
 
 [wheel]
 universal = 1
-
-[egg_info]
-tag_build = 
-tag_date = 0
-tag_svn_revision = 0
-
diff --git a/setup.py b/setup.py
index 6f5df4b..e923a9f 100644
--- a/setup.py
+++ b/setup.py
@@ -19,7 +19,7 @@ class Tox(TestCommand):
         self.test_suite = True
 
     def run_tests(self):
-        #import here, cause outside the eggs aren't loaded
+        # import here, cause outside the eggs aren't loaded
         import tox
         errno = tox.cmdline(self.test_args)
         sys.exit(errno)
@@ -32,7 +32,7 @@ with codecs.open('CHANGELOG.rst', encoding='utf-8') as f:
 
 setup(
     name='pypuppetdb',
-    version='0.1.1',
+    version='0.1.1+git080614',
     author='Daniele Sluijters',
     author_email='daniele.sluijters+pypi at gmail.com',
     packages=find_packages(),
diff --git a/test-requirements.txt b/test-requirements.txt
new file mode 100644
index 0000000..2c5db30
--- /dev/null
+++ b/test-requirements.txt
@@ -0,0 +1,6 @@
+requests==2.1.0
+pytest==2.5.0
+mock==1.0.1
+git+https://github.com/gabrielfalcao/HTTPretty.git@python-3.3-support#egg=httpretty
+pytest-pep8==1.0.5
+coverage==3.7
diff --git a/tests/test_baseapi.py b/tests/test_baseapi.py
new file mode 100644
index 0000000..cabb6f0
--- /dev/null
+++ b/tests/test_baseapi.py
@@ -0,0 +1,261 @@
+import json
+import mock
+import httpretty
+import pytest
+import requests
+import pypuppetdb
+
+
+def stub_request(url, data=None, **kwargs):
+    if data is None:
+        body = '[]'
+    else:
+        with open(data, 'r') as d:
+            body = json.load(d.read())
+    return httpretty.register_uri(httpretty.GET, url, body=body, status=200,
+                                  **kwargs)
+
+
+class TestBaseAPIVersion(object):
+    def test_init_v2_defaults(self):
+        v2 = pypuppetdb.api.BaseAPI(2)
+        assert v2.api_version == 'v2'
+
+    def test_init_v3_defaults(self):
+        v3 = pypuppetdb.api.BaseAPI(3)
+        assert v3.api_version == 'v3'
+
+    def test_init_invalid_version(self):
+        with pytest.raises(pypuppetdb.errors.UnsupportedVersionError):
+            vderp = pypuppetdb.api.BaseAPI(10000)
+
+
+class TestBaseAPIInitOptions(object):
+    def test_defaults(self, baseapi):
+        assert baseapi.host == 'localhost'
+        assert baseapi.port == 8080
+        assert baseapi.ssl_verify is True
+        assert baseapi.ssl_key is None
+        assert baseapi.ssl_cert is None
+        assert baseapi.timeout == 10
+        assert baseapi.protocol == 'http'
+
+    def test_host(self):
+        api = pypuppetdb.api.BaseAPI(3, host='127.0.0.1')
+        assert api.host == '127.0.0.1'
+
+    def test_port(self):
+        api = pypuppetdb.api.BaseAPI(3, port=8081)
+        assert api.port == 8081
+
+    def test_ssl_verify(self):
+        api = pypuppetdb.api.BaseAPI(3, ssl_verify=False)
+        assert api.ssl_verify is False
+        assert api.protocol == 'http'
+
+    def test_ssl_key(self):
+        api = pypuppetdb.api.BaseAPI(3, ssl_key='/a/b/c.pem')
+        assert api.ssl_key == '/a/b/c.pem'
+        assert api.protocol == 'http'
+
+    def test_ssl_cert(self):
+        api = pypuppetdb.api.BaseAPI(3, ssl_cert='/d/e/f.pem')
+        assert api.ssl_cert == '/d/e/f.pem'
+        assert api.protocol == 'http'
+
+    def test_ssl_key_and_cert(self):
+        api = pypuppetdb.api.BaseAPI(3, ssl_cert='/d/e/f.pem',
+                                     ssl_key='/a/b/c.pem')
+        assert api.ssl_key == '/a/b/c.pem'
+        assert api.ssl_cert == '/d/e/f.pem'
+        assert api.protocol == 'https'
+
+    def test_timeout(self):
+        api = pypuppetdb.api.BaseAPI(3, timeout=20)
+        assert api.timeout == 20
+
+
+class TestBaseAPIProperties(object):
+    def test_version(self, baseapi):
+        assert baseapi.version == 'v3'
+
+    def test_base_url(self, baseapi):
+        assert baseapi.base_url == 'http://localhost:8080'
+
+    def test_base_url_ssl(self, baseapi):
+        baseapi.protocol = 'https'  # slightly evil
+        assert baseapi.base_url == 'https://localhost:8080'
+
+    def test_total(self, baseapi):
+        baseapi.last_total = 10  # slightly evil
+        assert baseapi.total == 10
+
+
+class TestBaseAPIURL(object):
+    def test_without_path(self, baseapi):
+        assert baseapi._url('nodes') == 'http://localhost:8080/v3/nodes'
+
+    def test_with_invalid_endpoint(self, baseapi):
+        with pytest.raises(pypuppetdb.errors.APIError):
+            baseapi._url('this_will-Never+Ex1s7')
+
+    def test_with_path(self, baseapi):
+        url = baseapi._url('nodes', path='node1.example.com')
+        assert url == 'http://localhost:8080/v3/nodes/node1.example.com'
+
+
+class TesteAPIQuery(object):
+    @mock.patch.object(requests.Session, 'request')
+    def test_timeout(self, get, baseapi):
+        get.side_effect = requests.exceptions.Timeout
+        with pytest.raises(requests.exceptions.Timeout):
+            baseapi._query('nodes')
+
+    @mock.patch.object(requests.Session, 'request')
+    def test_connectionerror(self, get, baseapi):
+        get.side_effect = requests.exceptions.ConnectionError
+        with pytest.raises(requests.exceptions.ConnectionError):
+            baseapi._query('nodes')
+
+    @mock.patch.object(requests.Session, 'request')
+    def test_httperror(self, get, baseapi):
+        get.side_effect = requests.exceptions.HTTPError(
+            response=requests.Response())
+        with pytest.raises(requests.exceptions.HTTPError):
+            baseapi._query('nodes')
+
+    def test_setting_headers(self, baseapi):
+        httpretty.enable()
+        stub_request('http://localhost:8080/v3/nodes')
+        baseapi._query('nodes')  # need to query some endpoint
+        request_headers = dict(httpretty.last_request().headers)
+        assert request_headers['Accept'] == 'application/json'
+        assert request_headers['Content-Type'] == 'application/json'
+        assert request_headers['Accept-Charset'] == 'utf-8'
+        assert request_headers['Host'] == 'localhost:8080'
+        assert httpretty.last_request().path == '/v3/nodes'
+        httpretty.disable()
+        httpretty.reset()
+
+    def test_with_path(self, baseapi):
+        httpretty.enable()
+        stub_request('http://localhost:8080/v3/nodes/node1')
+        baseapi._query('nodes', path='node1')
+        assert httpretty.last_request().path == '/v3/nodes/node1'
+        httpretty.disable()
+        httpretty.reset()
+
+    def test_with_query(self, baseapi):
+        httpretty.enable()
+        stub_request('http://localhost:8080/v3/nodes')
+        baseapi._query('nodes', query='["certname", "=", "node1"]')
+        assert httpretty.last_request().querystring == {
+            'query': ['["certname", "=", "node1"]']}
+        httpretty.disable()
+        httpretty.reset()
+
+    def test_with_order(self, baseapi):
+        httpretty.enable()
+        stub_request('http://localhost:8080/v3/nodes')
+        baseapi._query('nodes', order_by='ted')
+        assert httpretty.last_request().querystring == {
+            'order-by': ['ted']}
+        httpretty.disable()
+        httpretty.reset()
+
+    def test_with_limit(self, baseapi):
+        httpretty.enable()
+        stub_request('http://localhost:8080/v3/nodes')
+        baseapi._query('nodes', limit=1)
+        assert httpretty.last_request().querystring == {
+            'limit': ['1']}
+        httpretty.disable()
+        httpretty.reset()
+
+    def test_with_include_total(self, baseapi):
+        httpretty.enable()
+        stub_request('http://localhost:8080/v3/nodes')
+        baseapi._query('nodes', include_total=True)
+        assert httpretty.last_request().querystring == {
+            'include-total': ['true']}
+        httpretty.disable()
+        httpretty.reset()
+
+    def test_with_offset(self, baseapi):
+        httpretty.enable()
+        stub_request('http://localhost:8080/v3/nodes')
+        baseapi._query('nodes', offset=1)
+        assert httpretty.last_request().querystring == {
+            'offset': ['1']}
+        httpretty.disable()
+        httpretty.reset()
+
+    def test_with_summarize_by(self, baseapi):
+        httpretty.enable()
+        stub_request('http://localhost:8080/v3/nodes')
+        baseapi._query('nodes', summarize_by=1)
+        assert httpretty.last_request().querystring == {
+            'summarize-by': ['1']}
+        httpretty.disable()
+        httpretty.reset()
+
+    def test_with_count_by(self, baseapi):
+        httpretty.enable()
+        stub_request('http://localhost:8080/v3/nodes')
+        baseapi._query('nodes', count_by=1)
+        assert httpretty.last_request().querystring == {
+            'count-by': ['1']}
+        httpretty.disable()
+        httpretty.reset()
+
+    def test_with_count_filter(self, baseapi):
+        httpretty.enable()
+        stub_request('http://localhost:8080/v3/nodes')
+        baseapi._query('nodes', count_filter=1)
+        assert httpretty.last_request().querystring == {
+            'count-filter': ['1']}
+        httpretty.disable()
+        httpretty.reset()
+
+    def test_response_empty(self, baseapi):
+        httpretty.enable()
+        httpretty.register_uri(httpretty.GET, 'http://localhost:8080/v3/nodes',
+                               body=json.dumps(None))
+        with pytest.raises(pypuppetdb.errors.EmptyResponseError):
+            baseapi._query('nodes')
+
+    def test_response_x_records(self, baseapi):
+        httpretty.enable()
+        httpretty.register_uri(httpretty.GET, 'http://localhost:8080/v3/nodes',
+                               adding_headers={
+                                   'X-Records': 256},
+                               body='[]',
+                               )
+        baseapi._query('nodes', include_total=True)
+        assert baseapi.total == 256
+
+
+class TestAPIMethods(object):
+    def test_nodes(self, baseapi):
+        with pytest.raises(NotImplementedError):
+            baseapi.nodes()
+
+    def test_node(self, baseapi):
+        with pytest.raises(NotImplementedError):
+            baseapi.node()
+
+    def test_facts(self, baseapi):
+        with pytest.raises(NotImplementedError):
+            baseapi.facts()
+
+    def test_resources(self, baseapi):
+        with pytest.raises(NotImplementedError):
+            baseapi.resources()
+
+    def test_metric(self, baseapi):
+        httpretty.enable()
+        stub_request('http://localhost:8080/v3/metrics/mbean/test')
+        baseapi.metric('test')
+        assert httpretty.last_request().path == '/v3/metrics/mbean/test'
+        httpretty.disable()
+        httpretty.reset()
diff --git a/tests/test_connect.py b/tests/test_connect.py
new file mode 100644
index 0000000..5beb2a5
--- /dev/null
+++ b/tests/test_connect.py
@@ -0,0 +1,17 @@
+import pytest
+import pypuppetdb
+
+
+def test_connect_unknown_api_version():
+    with pytest.raises(pypuppetdb.errors.UnsupportedVersionError):
+        pypuppetdb.connect(api_version=1)
+
+
+def test_connect_api_v2():
+    puppetdb = pypuppetdb.connect(api_version=2)
+    assert puppetdb.version == 'v2'
+
+
+def test_connect_api_v3():
+    puppetdb = pypuppetdb.connect(api_version=3)
+    assert puppetdb.version == 'v3'
diff --git a/tests/test_package.py b/tests/test_package.py
new file mode 100644
index 0000000..f492176
--- /dev/null
+++ b/tests/test_package.py
@@ -0,0 +1,11 @@
+from pypuppetdb.package import (
+    __title__, __author__, __license__,
+    __year__, __copyright__)
+
+
+def test_package():
+    assert __title__ == 'pypuppetdb'
+    assert __author__ == 'Daniele Sluijters'
+    assert __license__ == 'Apache License 2.0'
+    assert __year__ == '2013, 2014'
+    assert __copyright__ == 'Copyright {0} {1}'.format(__year__, __author__)
diff --git a/tests/test_types.py b/tests/test_types.py
new file mode 100644
index 0000000..ed01630
--- /dev/null
+++ b/tests/test_types.py
@@ -0,0 +1,185 @@
+import sys
+
+from pypuppetdb.utils import json_to_datetime
+from pypuppetdb.types import (
+    Node, Fact, Resource,
+    Report, Event, Catalog, Edge
+    )
+
+if sys.version_info >= (3, 0):
+    unicode = str
+
+
+class TestNode(object):
+    """Test the Node object."""
+    def test_without_status(self):
+        node = Node('_', 'node',
+                    report_timestamp='2013-08-01T09:57:00.000Z',
+                    catalog_timestamp='2013-08-01T09:57:00.000Z',
+                    facts_timestamp='2013-08-01T09:57:00.000Z',)
+
+        assert node.name == 'node'
+        assert node.deactivated is False
+        assert node.report_timestamp is not None
+        assert node.facts_timestamp is not None
+        assert node.catalog_timestamp is not None
+        assert str(node) == str('node')
+        assert unicode(node) == unicode('node')
+        assert repr(node) == str('<Node: node>')
+
+    def test_with_status_unreported(self):
+        node = Node('_', 'node',
+                    report_timestamp='2013-08-01T09:57:00.000Z',
+                    catalog_timestamp='2013-08-01T09:57:00.000Z',
+                    facts_timestamp='2013-08-01T09:57:00.000Z',
+                    status='unreported',
+                    unreported_time='0d 5h 20m',)
+
+        assert node.name == 'node'
+        assert node.deactivated is False
+        assert node.report_timestamp is not None
+        assert node.facts_timestamp is not None
+        assert node.catalog_timestamp is not None
+        assert node.status is 'unreported'
+        assert node.unreported_time is '0d 5h 20m'
+        assert str(node) == str('node')
+        assert unicode(node) == unicode('node')
+        assert repr(node) == str('<Node: node>')
+
+
+class TestFact(object):
+    """Test the Fact object."""
+    def test_fact(self):
+        fact = Fact('node', 'osfamily', 'Debian')
+
+        assert fact.node == 'node'
+        assert fact.name == 'osfamily'
+        assert fact.value == 'Debian'
+        assert str(fact) == str('osfamily/node')
+        assert unicode(fact) == unicode('osfamily/node')
+        assert repr(fact) == str('Fact: osfamily/node')
+
+
+class TestResource(object):
+    "Test the Resource object."""
+
+    def test_resource(self):
+        resource = Resource('node', '/etc/ssh/sshd_config', 'file',
+                            ['class', 'ssh'], False, '/ssh/manifests/init.pp',
+                            15, parameters={
+                                'ensure': 'present',
+                                'owner': 'root',
+                                'group': 'root',
+                                'mode': '0600',
+                                })
+
+        assert resource.node == 'node'
+        assert resource.name == '/etc/ssh/sshd_config'
+        assert resource.type_ == 'file'
+        assert resource.tags == ['class', 'ssh']
+        assert resource.exported is False
+        assert resource.sourcefile == '/ssh/manifests/init.pp'
+        assert resource.sourceline == 15
+        assert resource.parameters['ensure'] == 'present'
+        assert resource.parameters['owner'] == 'root'
+        assert resource.parameters['group'] == 'root'
+        assert resource.parameters['mode'] == '0600'
+        assert str(resource) == str('file[/etc/ssh/sshd_config]')
+        assert unicode(resource) == unicode('file[/etc/ssh/sshd_config]')
+        assert repr(resource) == str(
+            '<Resource: file[/etc/ssh/sshd_config]>')
+
+
+class TestReport(object):
+    """Test the Report object."""
+    def test_report(self):
+        report = Report('node1.puppet.board', 'hash#',
+                        '2013-08-01T09:57:00.000Z',
+                        '2013-08-01T10:57:00.000Z',
+                        '2013-08-01T10:58:00.000Z',
+                        '1351535883', 3, '3.2.1',
+                        'af9f16e3-75f6-4f90-acc6-f83d6524a6f3')
+
+        assert report.node == 'node1.puppet.board'
+        assert report.hash_ == 'hash#'
+        assert report.start == json_to_datetime('2013-08-01T09:57:00.000Z')
+        assert report.end == json_to_datetime('2013-08-01T10:57:00.000Z')
+        assert report.received == json_to_datetime('2013-08-01T10:58:00.000Z')
+        assert report.version == '1351535883'
+        assert report.format_ == 3
+        assert report.agent_version == '3.2.1'
+        assert report.run_time == report.end - report.start
+        assert report.transaction == 'af9f16e3-75f6-4f90-acc6-f83d6524a6f3'
+        assert str(report) == str('hash#')
+        assert unicode(report) == unicode('hash#')
+        assert repr(report) == str('Report: hash#')
+
+
+class TestEvent(object):
+    """Test the Event object."""
+    def test_event(self):
+        event = Event('node', 'failure', '2013-08-01T10:57:00.000Z',
+                      'hash#', '/etc/ssh/sshd_config', 'ensure',
+                      'Nothing to say', 'present', 'absent', 'file')
+
+        assert event.node == 'node'
+        assert event.status == 'failure'
+        assert event.failed is True
+        assert event.timestamp == json_to_datetime('2013-08-01T10:57:00.000Z')
+        assert event.hash_ == 'hash#'
+        assert event.item['title'] == '/etc/ssh/sshd_config'
+        assert event.item['type'] == 'file'
+        assert event.item['property'] == 'ensure'
+        assert event.item['message'] == 'Nothing to say'
+        assert event.item['old'] == 'absent'
+        assert event.item['new'] == 'present'
+        assert str(event) == str('file[/etc/ssh/sshd_config]/hash#')
+        assert unicode(event) == unicode('file[/etc/ssh/sshd_config]/hash#')
+        assert repr(event) == str('Event: file[/etc/ssh/sshd_config]/hash#')
+
+    def test_event_failed(self):
+        event = Event('node', 'success', '2013-08-01T10:57:00.000Z',
+                      'hash#', '/etc/ssh/sshd_config', 'ensure',
+                      'Nothing to say', 'present', 'absent', 'file')
+
+        assert event.status == 'success'
+        assert event.failed is False
+
+
+class TestCatalog(object):
+    """Test the Catalog object."""
+    def test_catalog(self):
+        catalog = Catalog('node', [], [], 'unique', None)
+        assert catalog.node == 'node'
+        assert catalog.version == 'unique'
+        assert catalog.transaction_uuid is None
+        assert catalog.resources == {}
+        assert catalog.edges == []
+        assert str(catalog) == str('node/None')
+        assert unicode(catalog) == unicode('node/None')
+        assert repr(catalog) == str(
+            '<Catalog: node/None>')
+
+
+class TestEdge(object):
+    """Test the Edge object."""
+    def test_edge(self):
+        resource_a = Resource('node', '/etc/ssh/sshd_config', 'file',
+                              ['class', 'ssh'], False,
+                              '/ssh/manifests/init.pp', 15, parameters={})
+
+        resource_b = Resource('node', 'sshd', 'service',
+                              ['class', 'ssh'], False,
+                              '/ssh/manifests/init.pp', 30, parameters={})
+
+        edge = Edge(resource_a, resource_b, 'notify')
+
+        assert edge.source == resource_a
+        assert edge.target == resource_b
+        assert edge.relationship == 'notify'
+        assert str(edge) == str(
+            'file[/etc/ssh/sshd_config] - notify - service[sshd]')
+        assert unicode(edge) == unicode(
+            'file[/etc/ssh/sshd_config] - notify - service[sshd]')
+        assert repr(edge) == str(
+            '<Edge: file[/etc/ssh/sshd_config] - notify - service[sshd]>')
diff --git a/tests/test_utils.py b/tests/test_utils.py
new file mode 100644
index 0000000..72498df
--- /dev/null
+++ b/tests/test_utils.py
@@ -0,0 +1,49 @@
+from __future__ import unicode_literals
+
+import sys
+
+import pytest
+import pypuppetdb
+import datetime
+
+if sys.version_info >= (3, 0):
+    unicode = str
+
+
+class TestUTC(object):
+    """Test the UTC class."""
+
+    def test_utc_offset(self, utc):
+        assert datetime.timedelta(0) == utc.utcoffset(300)
+
+    def test_tzname(self, utc):
+        assert str('UTC') == utc.tzname(300)
+
+    def test_dst(self, utc):
+        assert datetime.timedelta(0) == utc.dst(300)
+
+    def test_magic_str(self, utc):
+        assert str('UTC') == str(utc)
+
+    def test_magic_unicode(self, utc):
+        assert 'UTC' == unicode(utc)
+
+    def test_magic_repr(self, utc):
+        assert str('<UTC>') == repr(utc)
+
+
+class TestJSONToDateTime(object):
+    """Test the json_to_datetime function."""
+
+    def test_json_to_datetime(self):
+        json_datetime = '2013-08-01T09:57:00.000Z'
+        python_datetime = pypuppetdb.utils.json_to_datetime(json_datetime)
+        assert python_datetime.dst() == datetime.timedelta(0)
+        assert python_datetime.date() == datetime.date(2013, 8, 1)
+        assert python_datetime.tzname() == 'UTC'
+        assert python_datetime.utcoffset() == datetime.timedelta(0)
+        assert python_datetime.dst() == datetime.timedelta(0)
+
+    def test_json_to_datetime_invalid(self):
+        with pytest.raises(ValueError):
+            pypuppetdb.utils.json_to_datetime('2013-08-0109:57:00.000Z')
diff --git a/tox.ini b/tox.ini
new file mode 100644
index 0000000..3bcd69c
--- /dev/null
+++ b/tox.ini
@@ -0,0 +1,11 @@
+[tox]
+envlist = py26, py27, py33, pypy, pep8
+
+[testenv]
+commands = py.test -m 'unit'
+deps =
+    -r{toxinidir}/requirements.txt
+
+[testenv:pep8]
+basepython = python2.6
+commands = py.test --pep8 -m 'not (unit or integration)'

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-puppet/python-pypuppetdb.git



More information about the Pkg-puppet-devel mailing list