| #!/usr/bin/env python | |
| # | |
| # Copyright 2007 Google Inc. | |
| # | |
| # 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. | |
| """ | |
| try: | |
| import json | |
| except ImportError: | |
| import simplejson as json | |
| import logging | |
| from endpoints import api_backend | |
| from endpoints import api_config | |
| from endpoints 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): | |
| self.__registered_classes = set() | |
| self.__api_configs = set() | |
| self.__api_methods = {} | |
| def register_spi(self, config_contents): | |
| """Register a single SPI 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 | |
| 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_config.ApiConfigurationError( | |
| 'SPI 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 | |
| @staticmethod | |
| def definition_name(): | |
| """Override definition_name so that it is not BackendServiceImpl.""" | |
| return api_backend.BackendService.definition_name() | |
| def getApiConfigs(self, request): | |
| """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.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 | |
| 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() |