Skip to content

Commit

Permalink
Merge pull request #2 from aubergine-developers/Refactor/documentation
Browse files Browse the repository at this point in the history
Refactor/documentation
  • Loading branch information
magdanowak committed Mar 12, 2018
2 parents 14ea84f + 87989a2 commit af0689a
Show file tree
Hide file tree
Showing 23 changed files with 301 additions and 176 deletions.
54 changes: 43 additions & 11 deletions doc/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,52 @@
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Welcome to nadia's documentation!
nadia documentation
=================================

.. toctree::
:maxdepth: 2
:caption: Contents:
**nadia** is a small and lightweight library for creating `marshmallow <https://marshmallow.readthedocs.io/en/latest/>`_ schemas for objects defined in OpeanAPI 3 specifications.

Usage example
-------------
**nadia** 's usage is as simple as it gets. Suppose you have standard
`OpenAPI petstore example <https://github.com/OAI/OpenAPI-Specification/blob/master/examples/v3.0/petstore.yaml>`_ yaml saved in your current directory. Below short snippet will create Schema for the Pet object and use it to validate two example objects.

.. code-block:: python
import yaml
import nadia.api
with open('petstore.yaml') as petstore:
data = yaml.load(petstore)
builder = nadia.api.SchemaBuilder.create()
schema = builder.build(data['components']['schemas']['Pet'])
usage.rst
reference.rst
valid_pet = {'id': 100, 'name': 'Doggo', 'tag': 'sometag'}
invalid_pet = {'id': 'foo', 'name': 'Lessie', 'tag': ['tag1', 'tag2']}
print('Validation errors for Doggo: {}'.format(schema.validate({'content': valid_pet})))
print('Validation errors for Lessie: {}'.format(schema.validate({'content': invalid_pet})))
Indices and tables
==================
What can **nadia** do?
----------------------
* construct Schema for your data type provided as a dict read from your spec's yml or json file.
* skip you the hassle of redesigning your Schema definitions everytime your specification changes. Just load your specs and let **nadia** do the job for you.

What can't **nadia** do?
------------------------
* use json references in your yaml/json file - you have to resolve them yourself.
* validate your OpenAPI specification - but there are tools that can do that for you.
* generate webserver using some xyz framework.

.. toctree::
:caption: User documentation
:maxdepth: 4

userdoc/tutorial.rst

.. toctree::
:caption: Technical reference
:maxdepth: 4

* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
reference/nadia.rst
7 changes: 0 additions & 7 deletions doc/source/modules.rst

This file was deleted.

78 changes: 0 additions & 78 deletions doc/source/nadia.rst

This file was deleted.

8 changes: 0 additions & 8 deletions doc/source/reference.rst

This file was deleted.

7 changes: 7 additions & 0 deletions doc/source/reference/nadia.api.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
nadia.api module
================

.. automodule:: nadia.api
:members:
:undoc-members:
:show-inheritance:
7 changes: 7 additions & 0 deletions doc/source/reference/nadia.array.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
nadia.array module
==================

.. automodule:: nadia.array
:members:
:undoc-members:
:show-inheritance:
7 changes: 7 additions & 0 deletions doc/source/reference/nadia.builder_provider.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
nadia.builder\_provider module
==============================

.. automodule:: nadia.builder_provider
:members:
:undoc-members:
:show-inheritance:
7 changes: 7 additions & 0 deletions doc/source/reference/nadia.common.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
nadia.common module
===================

.. automodule:: nadia.common
:members:
:undoc-members:
:show-inheritance:
7 changes: 7 additions & 0 deletions doc/source/reference/nadia.exceptions.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
nadia.exceptions module
=======================

.. automodule:: nadia.exceptions
:members:
:undoc-members:
:show-inheritance:
7 changes: 7 additions & 0 deletions doc/source/reference/nadia.object.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
nadia.object module
===================

.. automodule:: nadia.object
:members:
:undoc-members:
:show-inheritance:
7 changes: 7 additions & 0 deletions doc/source/reference/nadia.primitives.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
nadia.primitives module
=======================

.. automodule:: nadia.primitives
:members:
:undoc-members:
:show-inheritance:
16 changes: 16 additions & 0 deletions doc/source/reference/nadia.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
nadia package
=============

Submodules
----------

.. toctree::

nadia.api
nadia.array
nadia.builder_provider
nadia.common
nadia.exceptions
nadia.object
nadia.primitives
nadia.schema
7 changes: 7 additions & 0 deletions doc/source/reference/nadia.schema.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
nadia.schema module
===================

.. automodule:: nadia.schema
:members:
:undoc-members:
:show-inheritance:
25 changes: 0 additions & 25 deletions doc/source/usage.rst

This file was deleted.

41 changes: 41 additions & 0 deletions doc/source/userdoc/tutorial.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
nadia Tutorial
==============


Basic concepts
--------------

The basic workflow with **nadia** consists of the following steps:

1. Load your yaml or json specification file.
2. Construct SchemaBuilder - **nadia** defines convenient method for creating the reasonable
one via factory :py:meth:`nadia.api.SchemaBuilder.create`
3. Extract object definitions for which you want to create schema from the loaded dictionary.
4. Construct schema for the extracted object. Note that the schema will contain top level `content`
field.
5. Use your schema to validate or serialize/deserialize objects.

Why do all schemas created by **nadia** have a top level `content` field?
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
The reason why nadia hides your real object in the `content` field is that
not all OpenAPI schemas can be expressed as :py:class:`marshmallow.Schema`.
For instance, consider the following schema:

.. code-block:: yml
type: array
items:
type: number
Such a data type can be modelled as :py:class:`marshmallow.field.List` but not as
a `Schema`. However, if you wrap it inside a larger object under `content` field, than a Schema
for such object can be constructed. This step is done automatically by **nadia** - even if it is
unneccesary, to make behaviour consistent for all objects.


Creating Schemabuilders
-----------------------
You can create a :py:class:`nadia.api.SchemaBuilder` instance in one of two ways:

1. You can explicitly initialize it by passing a object providings component builders (see :py:mod:`nadia.builder_provider`.
2. You can use :py:class:`nadia.api.SchemaBuilder.create` staticmethod to construct default builder using **nadia's** default builder providers.
33 changes: 23 additions & 10 deletions nadia/api.py
Original file line number Diff line number Diff line change
@@ -1,30 +1,43 @@
"""Main builder class."""
"""The nadia public api."""
from uuid import uuid4
from marshmallow import Schema
from nadia.builder_provider import BuilderProvider
from nadia.schema import NadiaSchema


class SchemaBuilder(object):
"""Class used for building schemas for given specification dict."""
"""Class used for building schemas from given specification dict.
:param builder_provider: an object providing builders via get_builder method.
Typically an instance of :py:class:`nadia.builder_provider.BuilderProvider`.
"""

def __init__(self, builder_provider):
self.builder_provider = builder_provider

def build(self, spec_dict):
"""Build schema for given specification dict."""
content_builder = self.builder_provider.get_builder(spec_dict.get('type', 'object'))
attrs = {'content': content_builder.build_schema(spec_dict)}
def build(self, spec):
"""Build schema from given specification dict.
:param spec: a dictionary containing specification of the object for which
Schema should be build.
:type spec: dict
:return: Schema corresponding to the object defined by spec
:rtype: :py:class:`marshmallow.Schema`
"""
content_builder = self.builder_provider.get_builder(spec.get('type', 'object'))
attrs = {'content': content_builder.build_schema(spec)}
return type('Object' + str(uuid4()), (NadiaSchema, ), attrs)()

@staticmethod
def create():
"""Create SchemaBuilder.
Note: this method is designed to be further extended as a factory method.
Later it should be able to accept some parameters ogerning creation of the
SchemaBuilder.
:rtype: :py:class:`nadia.api.SchemaBuilder`
.. note:: This method is designed to be further extended as a factory method.
Later it should be able to accept some parameters governing creation of the
SchemaBuilder. Currently it creates :py:class:`nadia.api.SchemaBuilder`
by passing default instance of :py:class:`nadia.builder_provider.BulderProvider`
to initializer.
"""
provider = BuilderProvider.get_default()
return SchemaBuilder(provider)
17 changes: 12 additions & 5 deletions nadia/array.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
"""Implementation of validation builder for array type."""
"""Functionallities related to building schemas for array type."""
from marshmallow import fields
from nadia.common import Builder


class ArrayBuilder(Builder):
"""Schema builder for array datatype."""

def build_schema(self, spec_dict):
"""Build Marshmallow schema for array datatype."""
item_body = spec_dict['items']
def build_schema(self, spec):
"""Build Marshmallow schema for array datatype.
:param spec: specification of array for which Schema should be build.
:type spec: dict
:returns: a List field constructed such that its properties correspond to the ones
defined in `spec`.
:rtype: :py:class:`marshmallow.fields.List`
"""
item_body = spec['items']
item_builder = self.builder_provider.get_builder(item_body['type'])
return fields.List(item_builder.build_schema(item_body), **self.translate_args(spec_dict))
return fields.List(item_builder.build_schema(item_body), **self.translate_args(spec))

0 comments on commit af0689a

Please sign in to comment.