From 3262ba7b827943d469bfd3f285cc636755c74737 Mon Sep 17 00:00:00 2001 From: Paul Millette Date: Thu, 6 Oct 2016 10:43:55 -0400 Subject: [PATCH] Generate api documentation using sphinx and autoflask - add autoflask to index.rst - add pydocs for all methods - move app out of AlmanachApi so autoflask can see it Change-Id: I83e1af74507091774d1eff86fa1d07a237d69729 --- almanach/adapters/api_route_v1.py | 184 ++++++++++++++++++++++++++++++ almanach/api.py | 6 +- doc/source/conf.py | 7 +- doc/source/index.rst | 7 ++ test-requirements.txt | 1 + tox.ini | 2 +- 6 files changed, 197 insertions(+), 10 deletions(-) diff --git a/almanach/adapters/api_route_v1.py b/almanach/adapters/api_route_v1.py index 1860f8c..bbf020c 100644 --- a/almanach/adapters/api_route_v1.py +++ b/almanach/adapters/api_route_v1.py @@ -90,6 +90,10 @@ def authenticated(api_call): @api.route("/info", methods=["GET"]) @to_json def get_info(): + """Display information about the current version and counts of entities in the database. + + :code 200 OK: Service is available + """ logging.info("Get application info") return controller.get_application_info() @@ -98,6 +102,21 @@ def get_info(): @authenticated @to_json def create_instance(project_id): + """Create an instance for a tenant. + + :arg uuid project_id: Tenant Uuid + :arg uuid id: The instance uuid + :arg datetime created_at: Y-m-d H:M:S.f + :arg uuid flavor: The flavor uuid + :arg str os_type: The OS type + :arg str os_distro: The OS distro + :arg str os_version: The OS version + :arg str name: The instance name + + :code 201 Created: Instance successfully created + :code 400 Bad Request: If request data has an invalid or missing field + :code 404 Not Found: If tenant does not exist + """ instance = jsonutils.loads(flask.request.data) logging.info("Creating instance for tenant %s with data %s", project_id, instance) controller.create_instance( @@ -119,6 +138,15 @@ def create_instance(project_id): @authenticated @to_json def delete_instance(instance_id): + """Delete the instance. + + :arg uuid instance_id: Instance Uuid + :arg datetime date: Y-m-d H:M:S.f + + :code 202 Accepted: Instance successfully deleted + :code 400 Bad Request: If request data has an invalid or missing field + :code 404 Not Found: If instance does not exist + """ data = jsonutils.loads(flask.request.data) logging.info("Deleting instance with id %s with data %s", instance_id, data) controller.delete_instance( @@ -133,6 +161,16 @@ def delete_instance(instance_id): @authenticated @to_json def resize_instance(instance_id): + """Re-size an instance when the instance flavor was changed in OpenStack. + + :arg uuid instance_id: Instance Uuid + :arg datetime date: Y-m-d H:M:S.f + :arg uuid flavor: The flavor uuid + + :code 200 OK: Instance successfully re-sized + :code 400 Bad Request: If request data has an invalid or missing field + :code 404 Not Found: If instance does not exist + """ instance = jsonutils.loads(flask.request.data) logging.info("Resizing instance with id %s with data %s", instance_id, instance) controller.resize_instance( @@ -148,6 +186,18 @@ def resize_instance(instance_id): @authenticated @to_json def rebuild_instance(instance_id): + """Rebuild an instance when the instance image was changed in OpenStack. + + :arg uuid instance_id: Instance Uuid + :arg str distro: The OS distro + :arg str version: The OS version + :arg str os_type: The OS type + :arg datetime rebuild_date: Y-m-d H:M:S.f + + :code 200 OK: Instance successfully rebuilt + :code 400 Bad Request: If request data has an invalid or missing field + :code 404 Not Found: If instance does not exist + """ instance = jsonutils.loads(flask.request.data) logging.info("Rebuilding instance with id %s with data %s", instance_id, instance) controller.rebuild_instance( @@ -165,6 +215,16 @@ def rebuild_instance(instance_id): @authenticated @to_json def list_instances(project_id): + """List instances for a tenant. + + :arg uuid project_id: Tenant Uuid + :arg datetime start: Y-m-d H:M:S.f + :arg datetime end: Y-m-d H:M:S.f + + :code 200 OK: instance list exists + :code 400 Bad Request: If request data has an invalid or missing field + :code 404 Not Found: If tenant does not exist. + """ start, end = get_period() logging.info("Listing instances between %s and %s", start, end) return controller.list_instances(project_id, start, end) @@ -174,6 +234,20 @@ def list_instances(project_id): @authenticated @to_json def create_volume(project_id): + """Create a volume for a tenant. + + :arg uuid project_id: Tenant Uuid + :arg uuid volume_id: The Volume Uuid + :arg datetime start: Y-m-d H:M:S.f + :arg uuid volume_type: The Volume Type Uuid + :arg str size: The Volume Size + :arg str volume_name: The Volume Name + :arg uuid attached_to: The Instance Uuid the volume is attached to + + :code 201 Created: Volume successfully created + :code 400 Bad Request: If request data has an invalid or missing field + :code 404 Not Found: If tenant does not exist. + """ volume = jsonutils.loads(flask.request.data) logging.info("Creating volume for tenant %s with data %s", project_id, volume) controller.create_volume( @@ -193,6 +267,15 @@ def create_volume(project_id): @authenticated @to_json def delete_volume(volume_id): + """Delete the volume. + + :arg uuid volume_id: Volume Uuid + :arg datetime date: Y-m-d H:M:S.f + + :code 202 Accepted: Volume successfully deleted + :code 400 Bad Request: If data invalid or missing + :code 404 Not Found: If volume does not exist. + """ data = jsonutils.loads(flask.request.data) logging.info("Deleting volume with id %s with data %s", volume_id, data) controller.delete_volume( @@ -207,6 +290,16 @@ def delete_volume(volume_id): @authenticated @to_json def resize_volume(volume_id): + """Re-size a volume when the volume size was changed in OpenStack. + + :arg uuid volume_id: Volume Uuid + :arg str size: The size of the volume + :arg datetime date: Y-m-d H:M:S.f + + :code 200 OK: Volume successfully re-sized + :code 400 Bad Request: If request data has an invalid or missing field + :code 404 Not Found: If volume does not exist. + """ volume = jsonutils.loads(flask.request.data) logging.info("Resizing volume with id %s with data %s", volume_id, volume) controller.resize_volume( @@ -222,6 +315,16 @@ def resize_volume(volume_id): @authenticated @to_json def attach_volume(volume_id): + """Update the attachments for a volume when the volume attachments have been changed in OpenStack. + + :arg uuid volume_id: Volume Uuid + :arg datetime date: Y-m-d H:M:S.f + :arg dict attachments: The volume attachments + + :code 200 OK: Volume successfully attached + :code 400 Bad Request: If request data has an invalid or missing field + :code 404 Not Found: If volume does not exist. + """ volume = jsonutils.loads(flask.request.data) logging.info("Attaching volume with id %s with data %s", volume_id, volume) controller.attach_volume( @@ -237,6 +340,16 @@ def attach_volume(volume_id): @authenticated @to_json def detach_volume(volume_id): + """Detaches a volume when the volume is detached in OpenStack. + + :arg uuid volume_id: Volume Uuid + :arg datetime date: Y-m-d H:M:S.f + :arg dict attachments: The volume attachments + + :code 200 OK: Volume successfully detached + :code 400 Bad Request: If request data has an invalid or missing field + :code 404 Not Found: If volume does not exist. + """ volume = jsonutils.loads(flask.request.data) logging.info("Detaching volume with id %s with data %s", volume_id, volume) controller.detach_volume( @@ -252,6 +365,16 @@ def detach_volume(volume_id): @authenticated @to_json def list_volumes(project_id): + """List volumes for a tenant. + + :arg uuid project_id: Tenant Uuid + :arg datetime start: Y-m-d H:M:S.f + :arg datetime end: Y-m-d H:M:S.f + + :code 200 OK: volume list exists + :code 400 Bad Request: If request data has an invalid or missing field + :code 404 Not Found: If tenant does not exist. + """ start, end = get_period() logging.info("Listing volumes between %s and %s", start, end) return controller.list_volumes(project_id, start, end) @@ -261,6 +384,16 @@ def list_volumes(project_id): @authenticated @to_json def list_entity(project_id): + """List instances and volumes for a tenant. + + :arg uuid project_id: Tenant Uuid + :arg datetime start: Y-m-d H:M:S.f + :arg datetime end: Y-m-d H:M:S.f + + :code 200 OK: instances and volumes list exists + :code 400 Bad Request: If request data has an invalid or missing field + :code 404 Not Found: If tenant does not exist. + """ start, end = get_period() logging.info("Listing entities between %s and %s", start, end) return controller.list_entities(project_id, start, end) @@ -270,6 +403,16 @@ def list_entity(project_id): @authenticated @to_json def update_instance_entity(instance_id): + """Update an instance entity. + + :arg uuid instance_id: Instance Uuid + :arg datetime start: Y-m-d H:M:S.f + :arg datetime end: Y-m-d H:M:S.f + + :code 200 OK: Entity successfully updated + :code 400 Bad Request: If request data has an invalid or missing field + :code 404 Not Found: If instance does not exist. + """ data = jsonutils.loads(flask.request.data) logging.info("Updating instance entity with id %s with data %s", instance_id, data) if 'start' in flask.request.args: @@ -283,6 +426,13 @@ def update_instance_entity(instance_id): @api.route("/entity/", methods=["HEAD"]) @authenticated def entity_exists(entity_id): + """Verify that an entity exists. + + :arg uuid entity_id: Entity Uuid + + :code 200 OK: if the entity exists + :code 404 Not Found: if the entity does not exist + """ logging.info("Does entity with id %s exists", entity_id) response = flask.Response('', 404) if controller.entity_exists(entity_id=entity_id): @@ -294,6 +444,13 @@ def entity_exists(entity_id): @authenticated @to_json def get_entity(entity_id): + """Get an entity. + + :arg uuid entity_id: Entity Uuid + + :code 200 OK: Entity exists + :code 404 Not Found: If the entity does not exist + """ return controller.get_all_entities_by_id(entity_id) @@ -301,6 +458,10 @@ def get_entity(entity_id): @authenticated @to_json def list_volume_types(): + """List volume types. + + :code 200 OK: Volume types exist + """ logging.info("Listing volumes types") return controller.list_volume_types() @@ -309,6 +470,14 @@ def list_volume_types(): @authenticated @to_json def get_volume_type(type_id): + """Get a volume type. + + :arg uuid type_id: Volume Type Uuid + + :code 200 OK: Volume type exists + :code 400 Bad Request: If request data has an invalid or missing field + :code 404 Not Found: If the volume type does not exist + """ logging.info("Get volumes type for id %s", type_id) return controller.get_volume_type(type_id) @@ -317,6 +486,14 @@ def get_volume_type(type_id): @authenticated @to_json def create_volume_type(): + """Create a volume type. + + :arg str type_id: The Volume Type id + :arg str type_name: The Volume Type name + + :code 201 Created: Volume successfully created + :code 400 Bad Request: If request data has an invalid or missing field + """ volume_type = jsonutils.loads(flask.request.data) logging.info("Creating volume type with data '%s'", volume_type) controller.create_volume_type( @@ -330,6 +507,13 @@ def create_volume_type(): @authenticated @to_json def delete_volume_type(type_id): + """Delete the volume type. + + :arg uuid type_id: Volume Type Uuid + + :code 202 Accepted: Volume successfully deleted + :code 404 Not Found: If volume type does not exist. + """ logging.info("Deleting volume type with id '%s'", type_id) controller.delete_volume_type(type_id) return flask.Response(status=202) diff --git a/almanach/api.py b/almanach/api.py index b22236c..356bc8d 100644 --- a/almanach/api.py +++ b/almanach/api.py @@ -19,13 +19,13 @@ from almanach.adapters import auth_adapter from almanach.adapters import database_adapter from almanach.core import controller +app = Flask("almanach") +app.register_blueprint(api_route.api) + class AlmanachApi(object): def run(self, host, port): api_route.controller = controller.Controller(database_adapter.DatabaseAdapter()) api_route.auth_adapter = auth_adapter.AuthenticationAdapter().factory() - app = Flask("almanach") - app.register_blueprint(api_route.api) - return app.run(host=host, port=port) diff --git a/doc/source/conf.py b/doc/source/conf.py index 98133ee..822b6d7 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -22,12 +22,7 @@ sys.path.insert(0, os.path.abspath('../..')) # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = [ - "sphinx.ext.autodoc", - "sphinx.ext.doctest", - "sphinx.ext.todo", - "sphinx.ext.coverage", - "sphinx.ext.ifconfig", - "sphinx.ext.viewcode", + 'sphinxcontrib.autohttp.flask' ] # autodoc generation is a bit aggressive and a nuisance when doing heavy diff --git a/doc/source/index.rst b/doc/source/index.rst index b17c20c..a8eeebf 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -260,3 +260,10 @@ Almanach will process those events: - :code:`volume.update.end` - :code:`volume.exists` - :code:`volume_type.create` + +API documentation +----------------- + +.. autoflask:: almanach.api:app + :undoc-static: + :include-empty-docstring: diff --git a/test-requirements.txt b/test-requirements.txt index 4c2a8fe..d0970bc 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -8,5 +8,6 @@ flexmock==0.9.4 mongomock==2.0.0 PyHamcrest==1.8.5 sphinx>=1.2.1,!=1.3b1,<1.3 # BSD +sphinxcontrib-httpdomain # BSD flake8>=2.5.4,<2.6.0 # MIT hacking<0.12,>=0.11.0 # Apache-2.0 diff --git a/tox.ini b/tox.ini index 8bdbaae..62fb5ea 100644 --- a/tox.ini +++ b/tox.ini @@ -17,7 +17,7 @@ deps = -r{toxinidir}/test-requirements.txt commands = flake8 {posargs} [testenv:docs] -commands = python setup.py build_sphinx +commands = python setup.py build_sphinx --fresh-env [flake8] show-source = True