• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python
2#
3# Copyright 2010 Google Inc.
4#
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#
17
18"""Webapp forms interface to ProtoRPC services.
19
20This webapp application is automatically configured to work with ProtoRPCs
21that have a configured protorpc.RegistryService.  This webapp is
22automatically added to the registry service URL at <registry-path>/forms
23(default is /protorpc/form) when configured using the
24service_handlers.service_mapping function.
25"""
26
27import os
28
29from .google_imports import template
30from .google_imports import webapp
31
32
33__all__ = ['FormsHandler',
34           'ResourceHandler',
35
36           'DEFAULT_REGISTRY_PATH',
37          ]
38
39_TEMPLATES_DIR = os.path.join(os.path.dirname(os.path.dirname(__file__)),
40                              'static')
41
42_FORMS_TEMPLATE = os.path.join(_TEMPLATES_DIR, 'forms.html')
43_METHODS_TEMPLATE = os.path.join(_TEMPLATES_DIR, 'methods.html')
44
45DEFAULT_REGISTRY_PATH = '/protorpc'
46
47
48class ResourceHandler(webapp.RequestHandler):
49  """Serves static resources without needing to add static files to app.yaml."""
50
51  __RESOURCE_MAP = {
52    'forms.js': 'text/javascript',
53    'jquery-1.4.2.min.js': 'text/javascript',
54    'jquery.json-2.2.min.js': 'text/javascript',
55  }
56
57  def get(self, relative):
58    """Serve known static files.
59
60    If static file is not known, will return 404 to client.
61
62    Response items are cached for 300 seconds.
63
64    Args:
65      relative: Name of static file relative to main FormsHandler.
66    """
67    content_type = self.__RESOURCE_MAP.get(relative, None)
68    if not content_type:
69      self.response.set_status(404)
70      self.response.out.write('Resource not found.')
71      return
72
73    path = os.path.join(_TEMPLATES_DIR, relative)
74    self.response.headers['Content-Type'] = content_type
75    static_file = open(path)
76    try:
77      contents = static_file.read()
78    finally:
79      static_file.close()
80    self.response.out.write(contents)
81
82
83class FormsHandler(webapp.RequestHandler):
84  """Handler for display HTML/javascript forms of ProtoRPC method calls.
85
86  When accessed with no query parameters, will show a web page that displays
87  all services and methods on the associated registry path.  Links on this
88  page fill in the service_path and method_name query parameters back to this
89  same handler.
90
91  When provided with service_path and method_name parameters will display a
92  dynamic form representing the request message for that method.  When sent,
93  the form sends a JSON request to the ProtoRPC method and displays the
94  response in the HTML page.
95
96  Attribute:
97    registry_path: Read-only registry path known by this handler.
98  """
99
100  def __init__(self, registry_path=DEFAULT_REGISTRY_PATH):
101    """Constructor.
102
103    When configuring a FormsHandler to use with a webapp application do not
104    pass the request handler class in directly.  Instead use new_factory to
105    ensure that the FormsHandler is created with the correct registry path
106    for each request.
107
108    Args:
109      registry_path: Absolute path on server where the ProtoRPC RegsitryService
110        is located.
111    """
112    assert registry_path
113    self.__registry_path = registry_path
114
115  @property
116  def registry_path(self):
117    return self.__registry_path
118
119  def get(self):
120    """Send forms and method page to user.
121
122    By default, displays a web page listing all services and methods registered
123    on the server.  Methods have links to display the actual method form.
124
125    If both parameters are set, will display form for method.
126
127    Query Parameters:
128      service_path: Path to service to display method of.  Optional.
129      method_name: Name of method to display form for.  Optional.
130    """
131    params = {'forms_path': self.request.path.rstrip('/'),
132              'hostname': self.request.host,
133              'registry_path': self.__registry_path,
134    }
135    service_path = self.request.get('path', None)
136    method_name = self.request.get('method', None)
137
138    if service_path and method_name:
139      form_template = _METHODS_TEMPLATE
140      params['service_path'] = service_path
141      params['method_name'] = method_name
142    else:
143      form_template = _FORMS_TEMPLATE
144
145    self.response.out.write(template.render(form_template, params))
146
147  @classmethod
148  def new_factory(cls, registry_path=DEFAULT_REGISTRY_PATH):
149    """Construct a factory for use with WSGIApplication.
150
151    This method is called automatically with the correct registry path when
152    services are configured via service_handlers.service_mapping.
153
154    Args:
155      registry_path: Absolute path on server where the ProtoRPC RegsitryService
156        is located.
157
158    Returns:
159      Factory function that creates a properly configured FormsHandler instance.
160    """
161    def forms_factory():
162      return cls(registry_path)
163    return forms_factory
164