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. Returns: True if the class has been registered and it's fine to add this configuration. False if this configuration shouldn't be added. """ methods = parsed_config.get('methods') if not methods: return True service_class = None for method in methods.itervalues(): rosy_method = method.get('rosyMethod') if rosy_method and '.' in rosy_method: method_class = rosy_method.split('.', 1)[0] if service_class is None: service_class = method_class elif service_class != method_class: raise api_config.ApiConfigurationError( 'SPI registered with multiple classes within one ' 'configuration (%s and %s). Each call to register_spi should ' 'only contain the methods from a single class. Call ' 'repeatedly for multiple classes.' % (service_class, method_class)) if service_class is not None: if service_class in self.__registered_classes: return False self.__registered_classes.add(service_class) return True
def __create_name_version_map(api_services): """Create a map from API name/version to Service class/factory. This creates a map from an API name and version to a list of remote.Service factories that implement that API. Args: api_services: A list of remote.Service-derived classes or factories created with remote.Service.new_factory. Returns: A mapping from (api name, api version) to a list of service factories, for service classes that implement that API. Raises: ApiConfigurationError: If a Service class appears more than once in api_services. """ api_name_version_map = {} for service_factory in api_services: try: service_class = service_factory.service_class except AttributeError: service_class = service_factory service_factory = service_class.new_factory() key = service_class.api_info.name, service_class.api_info.version service_factories = api_name_version_map.setdefault(key, []) if service_factory in service_factories: raise api_config.ApiConfigurationError( 'Can\'t add the same class to an API twice: %s' % service_factory.service_class.__name__) service_factories.append(service_factory) return api_name_version_map
def __init__(self, api_services, **kwargs): """Initialize an _ApiServer instance. The primary function of this method is to set up the WSGIApplication instance for the service handlers described by the services passed in. Additionally, it registers each API in ApiConfigRegistry for later use in the BackendService.getApiConfigs() (API config enumeration service). Args: api_services: List of protorpc.remote.Service classes implementing the API **kwargs: Passed through to protorpc.wsgi.service.service_handlers except: protocols - ProtoRPC protocols are not supported, and are disallowed. restricted - If True or unset, the API will only be allowed to serve to Google's API serving infrastructure once deployed. Set to False to allow other clients. Under dev_appserver, all clients are accepted. NOTE! Under experimental launch, this is not a secure restriction and other authentication mechanisms *must* be used to control access to the API. The restriction is only intended to notify developers of a possible upcoming feature to securely restrict access to the API. Raises: TypeError: if protocols are configured (this feature is not supported). ApiConfigurationError: if there's a problem with the API config. """ protorpc_services = [] generator = api_config.ApiConfigGenerator() self.api_config_registry = api_backend_service.ApiConfigRegistry() api_name_version_map = {} for service in api_services: key = (service.api_info.name, service.api_info.version) services = api_name_version_map.setdefault(key, []) if service in services: raise api_config.ApiConfigurationError( 'Can\'t add the same class to an API twice: %s' % service.__name__) services.append(service) for services in api_name_version_map.values(): config_file = generator.pretty_print_config_to_json(services) self.api_config_registry.register_spi(config_file) for api_service in services: protorpc_class_name = api_service.__name__ root = self.__SPI_PREFIX + protorpc_class_name if not any(service[0] == root or service[1] == api_service for service in protorpc_services): protorpc_services.append((root, api_service)) backend_service = api_backend_service.BackendServiceImpl.new_factory( self.api_config_registry, _get_app_revision()) protorpc_services.insert( 0, (self.__BACKEND_SERVICE_ROOT, backend_service)) if 'protocols' in kwargs: raise TypeError('__init__() got an unexpected keyword argument ' "'protocols'") self.restricted = kwargs.pop('restricted', True) self.service_app = wsgi_service.service_mappings( protorpc_services, **kwargs)
def __register_services(api_name_version_map, api_config_registry): """Register & return a list of each SPI URL and class that handles that URL. This finds every service class in api_name_version_map, registers it with the given ApiConfigRegistry, builds the SPI url for that class, and adds the URL and its factory to a list that's returned. Args: api_name_version_map: A mapping from (api name, api version) to a list of service factories, as returned by __create_name_version_map. api_config_registry: The ApiConfigRegistry where service classes will be registered. Returns: A list of (SPI URL, service_factory) for each service class in api_name_version_map. Raises: ApiConfigurationError: If a Service class appears more than once in api_name_version_map. This could happen if one class is used to implement multiple APIs. """ generator = api_config.ApiConfigGenerator() protorpc_services = [] for service_factories in api_name_version_map.itervalues(): service_classes = [ service_factory.service_class for service_factory in service_factories ] config_file = generator.pretty_print_config_to_json( service_classes) api_config_registry.register_spi(config_file) for service_factory in service_factories: protorpc_class_name = service_factory.service_class.__name__ root = _ApiServer.__SPI_PREFIX + protorpc_class_name if any(service_map[0] == root or service_map[1] == service_factory for service_map in protorpc_services): raise api_config.ApiConfigurationError( 'Can\'t reuse the same class in multiple APIs: %s' % protorpc_class_name) protorpc_services.append((root, service_factory)) return protorpc_services