blob: 40c3ffc28667eb1e9a124a79ff691ed0302f939f [file] [log] [blame]
Joe Gregorioc204b642010-09-21 12:01:23 -04001#!/usr/bin/env python
2#
Joe Gregorio9c8588a2011-07-01 12:01:36 -04003# Copyright 2011 Google Inc.
Joe Gregorioc204b642010-09-21 12:01:23 -04004#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16#
Joe Gregorio9c8588a2011-07-01 12:01:36 -040017"""Sample application for Python documentation of APIs.
18
19This is running live at http://api-python-client-doc.appspot.com where it
20provides a list of APIs and PyDoc documentation for all the generated API
21surfaces as they appear in the google-api-python-client. In addition it also
22provides a Google Gadget.
23"""
Joe Gregorioc204b642010-09-21 12:01:23 -040024
25__author__ = 'jcgregorio@google.com (Joe Gregorio)'
26
Joe Gregorioc5edb3c2011-03-18 10:54:10 -040027import httplib2
Joe Gregoriod44bb002010-09-21 14:34:44 -040028import inspect
Joe Gregorioc5edb3c2011-03-18 10:54:10 -040029import os
Joe Gregorioc204b642010-09-21 12:01:23 -040030import pydoc
31import re
32
Joe Gregorio9c8588a2011-07-01 12:01:36 -040033from apiclient import discovery
Joe Gregorio985e5402011-12-08 12:21:14 -050034from apiclient.errors import HttpError
Joe Gregorioc5edb3c2011-03-18 10:54:10 -040035from google.appengine.api import memcache
Joe Gregorioc204b642010-09-21 12:01:23 -040036from google.appengine.ext import webapp
Joe Gregorioc5edb3c2011-03-18 10:54:10 -040037from google.appengine.ext.webapp import template
Joe Gregorioc204b642010-09-21 12:01:23 -040038from google.appengine.ext.webapp import util
Joe Gregoriob4169b52012-02-14 14:42:01 -050039from oauth2client.anyjson import simplejson
Joe Gregorioc204b642010-09-21 12:01:23 -040040
Joe Gregoriod44bb002010-09-21 14:34:44 -040041
Joe Gregorio583d9e42011-09-16 15:54:15 -040042DISCOVERY_URI = 'https://www.googleapis.com/discovery/v1/apis?preferred=true'
43
44
45def get_directory_doc():
46 http = httplib2.Http(memcache)
47 ip = os.environ.get('REMOTE_ADDR', None)
48 uri = DISCOVERY_URI
49 if ip:
50 uri += ('&userIp=' + ip)
51 resp, content = http.request(uri)
52 directory = simplejson.loads(content)['items']
53 return directory
54
55
Joe Gregorioc204b642010-09-21 12:01:23 -040056class MainHandler(webapp.RequestHandler):
Joe Gregorio9c8588a2011-07-01 12:01:36 -040057 """Handles serving the main landing page.
58 """
Joe Gregorioc204b642010-09-21 12:01:23 -040059
60 def get(self):
Joe Gregorio583d9e42011-09-16 15:54:15 -040061 directory = get_directory_doc()
Joe Gregorio6a63a762011-05-02 22:36:05 -040062 for item in directory:
63 item['title'] = item.get('title', item.get('description', ''))
Joe Gregorioc5edb3c2011-03-18 10:54:10 -040064 path = os.path.join(os.path.dirname(__file__), 'index.html')
65 self.response.out.write(
66 template.render(
67 path, {'directory': directory,
68 }))
Joe Gregorioc204b642010-09-21 12:01:23 -040069
Joe Gregorio140062f2010-09-21 16:12:41 -040070
Joe Gregorio9c8588a2011-07-01 12:01:36 -040071class GadgetHandler(webapp.RequestHandler):
Joe Gregoriob4169b52012-02-14 14:42:01 -050072 """Handles serving the Google Gadget."""
Joe Gregorio9c8588a2011-07-01 12:01:36 -040073
74 def get(self):
Joe Gregorio583d9e42011-09-16 15:54:15 -040075 directory = get_directory_doc()
Joe Gregorio9c8588a2011-07-01 12:01:36 -040076 for item in directory:
77 item['title'] = item.get('title', item.get('description', ''))
78 path = os.path.join(os.path.dirname(__file__), 'gadget.html')
79 self.response.out.write(
80 template.render(
81 path, {'directory': directory,
82 }))
83 self.response.headers.add_header('Content-Type', 'application/xml')
84
85
Joe Gregoriob4169b52012-02-14 14:42:01 -050086class EmbedHandler(webapp.RequestHandler):
87 """Handles serving a front page suitable for embedding."""
88
89 def get(self):
90 directory = get_directory_doc()
91 for item in directory:
92 item['title'] = item.get('title', item.get('description', ''))
93 path = os.path.join(os.path.dirname(__file__), 'embed.html')
94 self.response.out.write(
95 template.render(
96 path, {'directory': directory,
97 }))
98
99
Joe Gregorio9c8588a2011-07-01 12:01:36 -0400100def _render(resource):
101 """Use pydoc helpers on an instance to generate the help documentation.
102 """
Joe Gregorioafc45f22011-02-20 16:11:28 -0500103 obj, name = pydoc.resolve(type(resource))
104 return pydoc.html.page(
105 pydoc.describe(obj), pydoc.html.document(obj, name))
106
Joe Gregorioc204b642010-09-21 12:01:23 -0400107
Joe Gregorio0fb19e12011-02-22 07:53:24 -0500108class ResourceHandler(webapp.RequestHandler):
Joe Gregorio9c8588a2011-07-01 12:01:36 -0400109 """Handles serving the PyDoc for a given collection.
110 """
Joe Gregorioc204b642010-09-21 12:01:23 -0400111
112 def get(self, service_name, version, collection):
Joe Gregorio583d9e42011-09-16 15:54:15 -0400113 http = httplib2.Http(memcache)
Joe Gregorio985e5402011-12-08 12:21:14 -0500114 try:
115 resource = discovery.build(service_name, version, http=http)
116 except:
117 return self.error(404)
Joe Gregorio20cfcda2010-10-26 11:58:08 -0400118 # descend the object path
Joe Gregorio0fb19e12011-02-22 07:53:24 -0500119 if collection:
Joe Gregorio985e5402011-12-08 12:21:14 -0500120 try:
121 path = collection.split('/')
122 if path:
123 for method in path:
124 resource = getattr(resource, method)()
125 except:
126 return self.error(404)
127
Joe Gregorio9c8588a2011-07-01 12:01:36 -0400128 page = _render(resource)
Joe Gregorio20cfcda2010-10-26 11:58:08 -0400129
Joe Gregorio0fb19e12011-02-22 07:53:24 -0500130 collections = []
131 for name in dir(resource):
132 if not "_" in name and callable(getattr(resource, name)) and hasattr(
133 getattr(resource, name), '__is_resource__'):
134 collections.append(name)
Joe Gregorio20cfcda2010-10-26 11:58:08 -0400135
Joe Gregorio0fb19e12011-02-22 07:53:24 -0500136 if collection is None:
137 collection_path = ''
138 else:
139 collection_path = collection + '/'
140 for name in collections:
141 page = re.sub('strong>(%s)<' % name,
142 r'strong><a href="/%s/%s/%s">\1</a><' % (
143 service_name, version, collection_path + name), page)
Joe Gregorio20cfcda2010-10-26 11:58:08 -0400144
Joe Gregorio0fb19e12011-02-22 07:53:24 -0500145 # TODO(jcgregorio) breadcrumbs
146 # TODO(jcgregorio) sample code?
147 page = re.sub('<p>', r'<a href="/">Home</a><p>', page, 1)
Joe Gregorioc204b642010-09-21 12:01:23 -0400148 self.response.out.write(page)
149
Joe Gregorio140062f2010-09-21 16:12:41 -0400150
Joe Gregorioc204b642010-09-21 12:01:23 -0400151def main():
152 application = webapp.WSGIApplication(
153 [
154 (r'/', MainHandler),
Joe Gregorio9c8588a2011-07-01 12:01:36 -0400155 (r'/_gadget/', GadgetHandler),
Joe Gregoriob4169b52012-02-14 14:42:01 -0500156 (r'/_embed/', EmbedHandler),
Joe Gregorio0fb19e12011-02-22 07:53:24 -0500157 (r'/([^\/]*)/([^\/]*)(?:/(.*))?', ResourceHandler),
Joe Gregorioc204b642010-09-21 12:01:23 -0400158 ],
Joe Gregorio985e5402011-12-08 12:21:14 -0500159 debug=False)
Joe Gregorioc204b642010-09-21 12:01:23 -0400160 util.run_wsgi_app(application)
161
162
163if __name__ == '__main__':
164 main()