Index: third_party/google-endpoints/endpoints/api_backend_service.py |
diff --git a/third_party/google-endpoints/endpoints/api_backend_service.py b/third_party/google-endpoints/endpoints/api_backend_service.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..ca7670a52c00f2306cd86958098d25897a667759 |
--- /dev/null |
+++ b/third_party/google-endpoints/endpoints/api_backend_service.py |
@@ -0,0 +1,184 @@ |
+# Copyright 2016 Google Inc. All Rights Reserved. |
+# |
+# Licensed under the Apache License, Version 2.0 (the "License"); |
+# you may not use this file except in compliance with the License. |
+# You may obtain a copy of the License at |
+# |
+# http://www.apache.org/licenses/LICENSE-2.0 |
+# |
+# Unless required by applicable law or agreed to in writing, software |
+# distributed under the License is distributed on an "AS IS" BASIS, |
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
+# See the License for the specific language governing permissions and |
+# limitations under the License. |
+ |
+"""API serving config collection service implementation. |
+ |
+Contains the implementation for BackendService as defined in api_backend.py. |
+""" |
+ |
+# pylint: disable=g-statement-before-imports,g-import-not-at-top |
+try: |
+ import json |
+except ImportError: |
+ import simplejson as json |
+ |
+import logging |
+ |
+import api_backend |
+import api_exceptions |
+ |
+from protorpc import message_types |
+ |
+__all__ = [ |
+ 'ApiConfigRegistry', |
+ 'BackendServiceImpl', |
+] |
+ |
+ |
+class ApiConfigRegistry(object): |
+ """Registry of active APIs to be registered with Google API Server.""" |
+ |
+ def __init__(self): |
+ # Set of API classes that have been registered. |
+ self.__registered_classes = set() |
+ # Set of API config contents served by this App Engine AppId/version |
+ self.__api_configs = set() |
+ # Map of API method name to ProtoRPC method name. |
+ self.__api_methods = {} |
+ |
+ # pylint: disable=g-bad-name |
+ def register_backend(self, config_contents): |
+ """Register a single API and its config contents. |
+ |
+ Args: |
+ config_contents: String containing API configuration. |
+ """ |
+ if config_contents is None: |
+ return |
+ parsed_config = json.loads(config_contents) |
+ self.__register_class(parsed_config) |
+ self.__api_configs.add(config_contents) |
+ self.__register_methods(parsed_config) |
+ |
+ def __register_class(self, parsed_config): |
+ """Register the class implementing this config, so we only add it once. |
+ |
+ Args: |
+ parsed_config: The JSON object with the API configuration being added. |
+ |
+ Raises: |
+ ApiConfigurationError: If the class has already been registered. |
+ """ |
+ methods = parsed_config.get('methods') |
+ if not methods: |
+ return |
+ |
+ # Determine the name of the class that implements this configuration. |
+ service_classes = set() |
+ for method in methods.itervalues(): |
+ rosy_method = method.get('rosyMethod') |
+ if rosy_method and '.' in rosy_method: |
+ method_class = rosy_method.split('.', 1)[0] |
+ service_classes.add(method_class) |
+ |
+ for service_class in service_classes: |
+ if service_class in self.__registered_classes: |
+ raise api_exceptions.ApiConfigurationError( |
+ 'API class %s has already been registered.' % service_class) |
+ self.__registered_classes.add(service_class) |
+ |
+ def __register_methods(self, parsed_config): |
+ """Register all methods from the given api config file. |
+ |
+ Methods are stored in a map from method_name to rosyMethod, |
+ the name of the ProtoRPC method to be called on the backend. |
+ If no rosyMethod was specified the value will be None. |
+ |
+ Args: |
+ parsed_config: The JSON object with the API configuration being added. |
+ """ |
+ methods = parsed_config.get('methods') |
+ if not methods: |
+ return |
+ |
+ for method_name, method in methods.iteritems(): |
+ self.__api_methods[method_name] = method.get('rosyMethod') |
+ |
+ def lookup_api_method(self, api_method_name): |
+ """Looks an API method up by name to find the backend method to call. |
+ |
+ Args: |
+ api_method_name: Name of the method in the API that was called. |
+ |
+ Returns: |
+ Name of the ProtoRPC method called on the backend, or None if not found. |
+ """ |
+ return self.__api_methods.get(api_method_name) |
+ |
+ def all_api_configs(self): |
+ """Return a list of all API configration specs as registered above.""" |
+ return list(self.__api_configs) |
+ |
+ |
+class BackendServiceImpl(api_backend.BackendService): |
+ """Implementation of BackendService.""" |
+ |
+ def __init__(self, api_config_registry, app_revision): |
+ """Create a new BackendService implementation. |
+ |
+ Args: |
+ api_config_registry: ApiConfigRegistry to register and look up configs. |
+ app_revision: string containing the current app revision. |
+ """ |
+ self.__api_config_registry = api_config_registry |
+ self.__app_revision = app_revision |
+ |
+ # pylint: disable=g-bad-name |
+ # pylint: disable=g-doc-return-or-yield |
+ # pylint: disable=g-doc-args |
+ @staticmethod |
+ def definition_name(): |
+ """Override definition_name so that it is not BackendServiceImpl.""" |
+ return api_backend.BackendService.definition_name() |
+ |
+ def getApiConfigs(self, request=None): |
+ """Return a list of active APIs and their configuration files. |
+ |
+ Args: |
+ request: A request which may contain an app revision |
+ |
+ Returns: |
+ ApiConfigList: A list of API config strings |
+ """ |
+ if (request and request.appRevision and |
+ request.appRevision != self.__app_revision): |
+ raise api_exceptions.BadRequestException( |
+ message='API backend app revision %s not the same as expected %s' % ( |
+ self.__app_revision, request.appRevision)) |
+ |
+ configs = self.__api_config_registry.all_api_configs() |
+ return api_backend.ApiConfigList(items=configs) |
+ |
+ def logMessages(self, request): |
+ """Write a log message from the Swarm FE to the log. |
+ |
+ Args: |
+ request: A log message request. |
+ |
+ Returns: |
+ Void message. |
+ """ |
+ Level = api_backend.LogMessagesRequest.LogMessage.Level |
+ log = logging.getLogger(__name__) |
+ for message in request.messages: |
+ level = message.level if message.level is not None else Level.info |
+ # Create a log record and override the pathname and lineno. These |
+ # messages come from the front end, so it's misleading to say that they |
+ # come from api_backend_service. |
+ record = logging.LogRecord(name=__name__, level=level.number, pathname='', |
+ lineno='', msg=message.message, args=None, |
+ exc_info=None) |
+ log.handle(record) |
+ |
+ return message_types.VoidMessage() |