def _generate_api_config_with_root(self, request):
    """Generate an API config with a specific root hostname.

    This uses the backend object and the ApiConfigGenerator to create an API
    config specific to the hostname of the incoming request. This allows for
    flexible API configs for non-standard environments, such as localhost.

    Args:
      request: An ApiRequest, the transformed request sent to the Discovery API.

    Returns:
      A string representation of the generated API config.
    """
    actual_root = self._get_actual_root(request)
    generator = api_config.ApiConfigGenerator()
    api = request.body_json['api']
    version = request.body_json['version']
    lookup_key = (api, version)

    service_factories = self._backend.api_name_version_map.get(lookup_key)
    if not service_factories:
      return None

    service_classes = [service_factory.service_class
                       for service_factory in service_factories]
    config_dict = generator.get_config_dict(
        service_classes, hostname=actual_root)

    # Save to cache
    for config in config_dict.get('items', []):
      lookup_key_with_root = (
          config.get('name', ''), config.get('version', ''), actual_root)
      self._config_manager.save_config(lookup_key_with_root, config)

    return config_dict
def GenApiConfig(service_class_names, config_string_generator=None,
                 hostname=None, application_path=None):
  """Write an API configuration for endpoints annotated ProtoRPC services.

  Args:
    service_class_names: A list of fully qualified ProtoRPC service classes.
    config_string_generator: A generator object that produces API config strings
      using its pretty_print_config_to_json method.
    hostname: A string hostname which will be used as the default version
      hostname. If no hostname is specificied in the @endpoints.api decorator,
      this value is the fallback.
    application_path: A string with the path to the AppEngine application.

  Raises:
    TypeError: If any service classes don't inherit from remote.Service.
    messages.DefinitionNotFoundError: If a service can't be found.

  Returns:
    A map from service names to a string containing the API configuration of the
      service in JSON format.
  """
  # First, gather together all the different APIs implemented by these
  # classes.  There may be fewer APIs than service classes.  Each API is
  # uniquely identified by (name, version).  Order needs to be preserved here,
  # so APIs that were listed first are returned first.
  api_service_map = collections.OrderedDict()
  for service_class_name in service_class_names:
    module_name, base_service_class_name = service_class_name.rsplit('.', 1)
    module = __import__(module_name, fromlist=base_service_class_name)
    service = getattr(module, base_service_class_name)
    if not isinstance(service, type) or not issubclass(service, remote.Service):
      raise TypeError('%s is not a ProtoRPC service' % service_class_name)

    services = api_service_map.setdefault(
        (service.api_info.name, service.api_info.version), [])
    services.append(service)

  # If hostname isn't specified in the API or on the command line, we'll
  # try to build it from information in app.yaml.
  app_yaml_hostname = _GetAppYamlHostname(application_path)

  service_map = collections.OrderedDict()
  config_string_generator = (
      config_string_generator or api_config.ApiConfigGenerator())
  for api_info, services in api_service_map.iteritems():
    assert services, 'An API must have at least one ProtoRPC service'
    # Only override hostname if None.  Hostname will be the same for all
    # services within an API, since it's stored in common info.
    hostname = services[0].api_info.hostname or hostname or app_yaml_hostname

    # Map each API by name-version.
    service_map['%s-%s' % api_info] = (
        config_string_generator.pretty_print_config_to_json(
            services, hostname=hostname))

  return service_map
Example #3
0
    def __register_services(api_name_version_map, api_config_registry):
        """Register & return a list of each 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 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 (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_backend(config_file)

            for service_factory in service_factories:
                protorpc_class_name = service_factory.service_class.__name__
                root = '%s%s' % (service_factory.service_class.api_info.
                                 base_path, 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