← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~benji/launchpad/wadl-refactoring into lp:launchpad/devel

 

Benji York has proposed merging lp:~benji/launchpad/wadl-refactoring into lp:launchpad/devel.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)


This branch extracts the refactorings and tests from an earlier branch
that we've decided to scrap.  All of the code in this branch has been
previously reviewed and approved.

The theme of the refactorings is to provide APIs to generate the WADL
and the web service documentation.  The branch also includes smoke tests
to be sure that something resembling HTML and WADL are generated and no
exceptions are raised.

Some whitespace lint was fixed in this branch.

To run the added tests: bin/test -c test_wadl_generation

-- 
https://code.launchpad.net/~benji/launchpad/wadl-refactoring/+merge/36452
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~benji/launchpad/wadl-refactoring into lp:launchpad/devel.
=== modified file 'Makefile'
--- Makefile	2010-09-07 18:15:01 +0000
+++ Makefile	2010-09-23 14:56:00 +0000
@@ -60,7 +60,7 @@
 
 $(API_INDEX): $(BZR_VERSION_INFO)
 	mkdir -p $(APIDOC_DIR).tmp
-	LPCONFIG=$(LPCONFIG) $(PY) ./utilities/create-lp-wadl-and-apidoc.py "$(WADL_TEMPLATE)"
+	LPCONFIG=$(LPCONFIG) $(PY) ./utilities/create-lp-wadl-and-apidoc.py --force "$(WADL_TEMPLATE)"
 	mv $(APIDOC_DIR).tmp $(APIDOC_DIR)
 
 apidoc: compile $(API_INDEX)

=== added file 'lib/canonical/launchpad/ftests/test_wadl_generation.py'
--- lib/canonical/launchpad/ftests/test_wadl_generation.py	1970-01-01 00:00:00 +0000
+++ lib/canonical/launchpad/ftests/test_wadl_generation.py	2010-09-23 14:56:00 +0000
@@ -0,0 +1,38 @@
+# Copyright 2009 Canonical Ltd.  This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""Tests for the web service WADL and HTML generation APIs."""
+
+__metaclass__ = type
+
+import pkg_resources
+import unittest
+
+from zope.component import getUtility
+
+from canonical.launchpad.rest.wadl import generate_wadl, generate_html
+from canonical.launchpad.systemhomes import WebServiceApplication
+from canonical.testing import LaunchpadFunctionalLayer
+from lazr.restful.interfaces import IWebServiceConfiguration
+
+
+class SmokeTestWadlAndDocGeneration(unittest.TestCase):
+    """Smoke test the WADL and HTML generation front-end functions."""
+
+    layer = LaunchpadFunctionalLayer
+
+    def test_wadl(self):
+        config = getUtility(IWebServiceConfiguration)
+        for version in config.active_versions:
+            wadl = generate_wadl(version)
+            self.assert_(wadl.startswith('<?xml '))
+
+    def test_html(self):
+        config = getUtility(IWebServiceConfiguration)
+        stylesheet = pkg_resources.resource_filename(
+            'launchpadlib', 'wadl-to-refhtml.xsl')
+        for version in config.active_versions:
+            wadl_filename = WebServiceApplication.cachedWADLPath(
+                'development', version)
+            html = generate_html(wadl_filename)
+            self.assert_('<html ' in html)

=== added file 'lib/canonical/launchpad/rest/wadl.py'
--- lib/canonical/launchpad/rest/wadl.py	1970-01-01 00:00:00 +0000
+++ lib/canonical/launchpad/rest/wadl.py	2010-09-23 14:56:00 +0000
@@ -0,0 +1,53 @@
+# Copyright 2010 Canonical Ltd.  This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""APIs to generate the web sercice WADL and documentation HTML."""
+
+__metaclass__ = type
+
+import pkg_resources
+import subprocess
+import urlparse
+
+from canonical.launchpad.webapp.interaction import (
+    ANONYMOUS,
+    setupInteractionByEmail,
+    )
+from canonical.launchpad.webapp.servers import (
+    WebServicePublication,
+    WebServiceTestRequest,
+    )
+from canonical.launchpad.webapp.vhosts import allvhosts
+
+
+def generate_wadl(version):
+    """Generate the WADL for the given version of the web service."""
+    url = urlparse.urljoin(allvhosts.configs['api'].rooturl, version)
+    # Since we want HTTPS URLs we have to munge the request URL.
+    url = url.replace('http://', 'https://')
+    request = WebServiceTestRequest(version=version, environ={
+        'SERVER_URL': url,
+        'HTTP_HOST': allvhosts.configs['api'].hostname,
+        'HTTP_ACCEPT': 'application/vd.sun.wadl+xml',
+        })
+    # We then bypass the usual publisher processing by associating
+    # the request with the WebServicePublication (usually done by the
+    # publisher) and then calling the root resource - retrieved
+    # through getApplication().
+    request.setPublication(WebServicePublication(None))
+    setupInteractionByEmail(ANONYMOUS, request)
+    return request.publication.getApplication(request)(request)
+
+
+def generate_html(wadl_filename):
+    """Given a WADL file generate HTML documentation from it."""
+    # If we're supposed to prevent the subprocess from generating output on
+    # stderr (like we want to do during test runs), we reassign the subprocess
+    # stderr file handle and then discard the output.  Otherwise we let the
+    # subprocess inherit stderr.
+    stylesheet = pkg_resources.resource_filename(
+        'launchpadlib', 'wadl-to-refhtml.xsl')
+    return subprocess.Popen(
+        ['xsltproc', stylesheet, wadl_filename],
+        stdout=subprocess.PIPE,
+        stderr=subprocess.PIPE).communicate()[0]

=== modified file 'utilities/create-lp-wadl-and-apidoc.py'
--- utilities/create-lp-wadl-and-apidoc.py	2010-09-05 20:29:40 +0000
+++ utilities/create-lp-wadl-and-apidoc.py	2010-09-23 14:56:00 +0000
@@ -1,90 +1,91 @@
 #! /usr/bin/python -S
 #
-# Copyright 2009 Canonical Ltd.  This software is licensed under the
+# Copyright 2010 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 """Create a static WADL file describing the current webservice.
 
-Usage hint:
+Example:
 
-% LPCONFIG="edge" utilities/create-lp-wadl.py launchpad-%(version)s.wadl
+    % LPCONFIG=development bin/py utilities/create-lp-wadl-and-apidoc.py \\
+      "lib/canonical/launchpad/apidoc/wadl-development-%(version)s.xml"
 """
 
-import _pythonpath
-
+import optparse
 import os
-import pkg_resources
-import subprocess
 import sys
-import urlparse
 
 from zope.component import getUtility
 from zope.pagetemplate.pagetemplatefile import PageTemplateFile
 
+from canonical.launchpad.rest.wadl import generate_wadl, generate_html
 from canonical.launchpad.scripts import execute_zcml_for_scripts
-from canonical.launchpad.webapp.interaction import (
-    ANONYMOUS, setupInteractionByEmail)
-from canonical.launchpad.webapp.servers import (
-    WebServicePublication, WebServiceTestRequest)
-from canonical.launchpad.webapp.vhosts import allvhosts
 from canonical.launchpad.systemhomes import WebServiceApplication
 from lazr.restful.interfaces import IWebServiceConfiguration
 
 
-def main(path_template):
+def write(filename, content):
+    """Replace the named file with the given string."""
+    f = open(filename, 'w')
+    f.write(content)
+    f.close()
+
+
+def main(path_template, force=False):
     WebServiceApplication.cached_wadl = None # do not use cached file version
     execute_zcml_for_scripts()
     config = getUtility(IWebServiceConfiguration)
-    directory, ignore = os.path.split(path_template)
-
-    stylesheet = pkg_resources.resource_filename(
-        'launchpadlib', 'wadl-to-refhtml.xsl')
+    directory = os.path.dirname(path_template)
 
     # First, create an index.html with links to all the HTML
     # documentation files we're about to generate.
     template_file = 'apidoc-index.pt'
     template = PageTemplateFile(template_file)
-    f = open(os.path.join(directory, "index.html"), 'w')
+    index_filename = os.path.join(directory, "index.html")
+    print "Writing index:", index_filename
+    f = open(index_filename, 'w')
     f.write(template(config=config))
 
-    # Request the WADL from the root resource.
-    # We do this by creating a request object asking for a WADL
-    # representation.
     for version in config.active_versions:
-        url = urlparse.urljoin(allvhosts.configs['api'].rooturl, version)
-        request = WebServiceTestRequest(version=version, environ={
-            'SERVER_URL': url,
-            'HTTP_HOST': allvhosts.configs['api'].hostname,
-            'HTTP_ACCEPT': 'application/vd.sun.wadl+xml'
-            })
-        # We then bypass the usual publisher processing by associating
-        # the request with the WebServicePublication (usually done  by the
-        # publisher) and then calling the root resource - retrieved through
-        # getApplication().
-        request.setPublication(WebServicePublication(None))
-        setupInteractionByEmail(ANONYMOUS, request)
-        filename = path_template % {'version' : version}
-        print "Writing WADL for version %s to %s." % (version, filename)
-        f = open(filename, 'w')
-        content = request.publication.getApplication(request)(request)
-        f.write(content)
-        f.close()
+        wadl_filename = path_template % {'version': version}
+        # If the WADL file doesn't exist or we're being forced to regenerate
+        # it...
+        if (not os.path.exists(wadl_filename) or force):
+            print "Writing WADL for version %s to %s." % (
+                version, wadl_filename)
+            write(wadl_filename, generate_wadl(version))
+        else:
+            print "Skipping already present WADL file:", wadl_filename
 
         # Now, convert the WADL into an human-readable description and
         # put the HTML in the same directory as the WADL.
         html_filename = os.path.join(directory, version + ".html")
-        print "Writing apidoc for version %s to %s" % (
-            version, html_filename)
-        stdout = open(html_filename, "w")
-        subprocess.Popen(['xsltproc', stylesheet, filename], stdout=stdout)
-        stdout.close()
+        # If the HTML file doesn't exist or we're being forced to regenerate
+        # it...
+        if (not os.path.exists(html_filename) or force):
+            print "Writing apidoc for version %s to %s" % (
+                version, html_filename)
+            write(html_filename, generate_html(wadl_filename))
+        else:
+            print "Skipping already present HTML file:", html_filename
 
     return 0
 
+
+def parse_args(args):
+    usage = "usage: %prog [options] PATH_TEMPLATE"
+    parser = optparse.OptionParser(usage=usage)
+    parser.add_option(
+        "--force", action="store_true",
+        help="Replace any already-existing files.")
+    parser.set_defaults(force=False)
+    options, args = parser.parse_args(args)
+    if len(args) != 2:
+        parser.error("A path template is required.")
+
+    return options, args
+
+
 if __name__ == '__main__':
-    if len(sys.argv) != 2:
-        print "Usage: %s [WADL path template]" % sys.argv[0]
-        print " Example: %s path/to/wadl/wadl-%%(version).xml" % (
-            sys.argv[0])
-        sys.exit(-1)
-    sys.exit(main(sys.argv[1]))
+    options, args = parse_args(sys.argv)
+    sys.exit(main(args[1], options.force))