def __get_merged_api_info(self, services):
        """Builds a description of an API.

    Args:
      services: List of protorpc.remote.Service instances implementing an
        api/version.

    Returns:
      The _ApiInfo object to use for the API that the given services implement.

    Raises:
      ApiConfigurationError: If there's something wrong with the API
        configuration, such as a multiclass API decorated with different API
        descriptors (see the docstring for api()).
    """
        merged_api_info = services[0].api_info

        # Verify that, if there are multiple classes here, they're allowed to
        # implement the same API.
        for service in services[1:]:
            if not merged_api_info.is_same_api(service.api_info):
                raise api_exceptions.ApiConfigurationError(
                    _MULTICLASS_MISMATCH_ERROR_TEMPLATE %
                    (service.api_info.name, service.api_info.version))

        return merged_api_info
  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 __get_merged_api_info(self, services):
        """Builds a description of an API.

    Args:
      services: List of protorpc.remote.Service instances implementing an
        api/version.

    Returns:
      The _ApiInfo object to use for the API that the given services implement.
    """
        base_paths = sorted(set(s.api_info.base_path for s in services))
        if len(base_paths) != 1:
            raise api_exceptions.ApiConfigurationError(
                'Multiple base_paths found: {!r}'.format(base_paths))
        names_versions = sorted(
            set((s.api_info.name, s.api_info.version) for s in services))
        if len(names_versions) != 1:
            raise api_exceptions.ApiConfigurationError(
                'Multiple apis/versions found: {!r}'.format(names_versions))
        return services[0].api_info
示例#4
0
 def __x_security_descriptor(self, audiences, security_definitions):
     default_auth_issuer = 'google_id_token'
     if isinstance(audiences, list):
         if default_auth_issuer not in security_definitions:
             raise api_exceptions.ApiConfigurationError(
                 _INVALID_AUTH_ISSUER % default_auth_issuer)
         return [{
             default_auth_issuer: {
                 'audiences': audiences,
             }
         }]
     elif isinstance(audiences, dict):
         descriptor = list()
         for audience_key, audience_value in audiences.items():
             if audience_key not in security_definitions:
                 raise api_exceptions.ApiConfigurationError(
                     _INVALID_AUTH_ISSUER % audience_key)
             descriptor.append(
                 {audience_key: {
                     'audiences': audience_value
                 }})
         return descriptor
    def __discovery_doc_descriptor(self, services, hostname=None):
        """Builds a discovery doc for an API.

    Args:
      services: List of protorpc.remote.Service instances implementing an
        api/version.
      hostname: string, Hostname of the API, to override the value set on the
        current service. Defaults to None.

    Returns:
      A dictionary that can be deserialized into JSON in discovery doc format.

    Raises:
      ApiConfigurationError: If there's something wrong with the API
        configuration, such as a multiclass API decorated with different API
        descriptors (see the docstring for api()), or a repeated method
        signature.
    """
        merged_api_info = self.__get_merged_api_info(services)
        descriptor = self.get_descriptor_defaults(merged_api_info,
                                                  hostname=hostname)

        description = merged_api_info.description
        if not description and len(services) == 1:
            description = services[0].__doc__
        if description:
            descriptor['description'] = description

        descriptor['parameters'] = self.__standard_parameters_descriptor()
        descriptor['auth'] = self.__standard_auth_descriptor()

        # Add namespace information, if provided
        if merged_api_info.namespace:
            descriptor['ownerDomain'] = merged_api_info.namespace.owner_domain
            descriptor['ownerName'] = merged_api_info.namespace.owner_name
            descriptor[
                'packagePath'] = merged_api_info.namespace.package_path or ''

        method_map = {}
        method_collision_tracker = {}
        rest_collision_tracker = {}

        resource_index = collections.defaultdict(list)
        resource_map = {}

        # For the first pass, only process top-level methods (that is, those methods
        # that are unattached to a resource).
        for service in services:
            remote_methods = service.all_remote_methods()

            for protorpc_meth_name, protorpc_meth_info in remote_methods.iteritems(
            ):
                method_info = getattr(protorpc_meth_info, 'method_info', None)
                # Skip methods that are not decorated with @method
                if method_info is None:
                    continue
                path = method_info.get_path(service.api_info)
                method_id = method_info.method_id(service.api_info)
                canonical_method_id = self._get_canonical_method_id(method_id)
                resource_path = self._get_resource_path(method_id)

                # Make sure the same method name isn't repeated.
                if method_id in method_collision_tracker:
                    raise api_exceptions.ApiConfigurationError(
                        'Method %s used multiple times, in classes %s and %s' %
                        (method_id, method_collision_tracker[method_id],
                         service.__name__))
                else:
                    method_collision_tracker[method_id] = service.__name__

                # Make sure the same HTTP method & path aren't repeated.
                rest_identifier = (method_info.http_method, path)
                if rest_identifier in rest_collision_tracker:
                    raise api_exceptions.ApiConfigurationError(
                        '%s path "%s" used multiple times, in classes %s and %s'
                        % (method_info.http_method, path,
                           rest_collision_tracker[rest_identifier],
                           service.__name__))
                else:
                    rest_collision_tracker[rest_identifier] = service.__name__

                # If this method is part of a resource, note it and skip it for now
                if resource_path:
                    resource_index[resource_path[0]].append(
                        (service, protorpc_meth_info))
                else:
                    method_map[canonical_method_id] = self.__method_descriptor(
                        service, method_info, protorpc_meth_info)

        # Do another pass for methods attached to resources
        for resource, resource_methods in resource_index.items():
            resource_map[resource] = self.__resource_descriptor(
                resource, resource_methods)

        if method_map:
            descriptor['methods'] = method_map

        if resource_map:
            descriptor['resources'] = resource_map

        # Add schemas, if any
        schemas = self.__schemas_descriptor()
        if schemas:
            descriptor['schemas'] = schemas

        return descriptor
示例#6
0
    def __api_swagger_descriptor(self, services, hostname=None):
        """Builds a Swagger description of an API.

    Args:
      services: List of protorpc.remote.Service instances implementing an
        api/version.
      hostname: string, Hostname of the API, to override the value set on the
        current service. Defaults to None.

    Returns:
      A dictionary that can be deserialized into JSON and stored as an API
      description document in Swagger format.

    Raises:
      ApiConfigurationError: If there's something wrong with the API
        configuration, such as a multiclass API decorated with different API
        descriptors (see the docstring for api()), or a repeated method
        signature.
    """
        merged_api_info = self.__get_merged_api_info(services)
        descriptor = self.get_descriptor_defaults(merged_api_info,
                                                  hostname=hostname)

        description = merged_api_info.description
        if not description and len(services) == 1:
            description = services[0].__doc__
        if description:
            descriptor['info']['description'] = description

        security_definitions = self.__security_definitions_descriptor(
            merged_api_info.issuers)

        method_map = {}
        method_collision_tracker = {}
        rest_collision_tracker = {}

        for service in services:
            remote_methods = service.all_remote_methods()

            for protorpc_meth_name, protorpc_meth_info in remote_methods.iteritems(
            ):
                method_info = getattr(protorpc_meth_info, 'method_info', None)
                # Skip methods that are not decorated with @method
                if method_info is None:
                    continue
                method_id = method_info.method_id(service.api_info)
                path = '/{0}/{1}/{2}'.format(
                    merged_api_info.name, merged_api_info.version,
                    method_info.get_path(service.api_info))
                verb = method_info.http_method.lower()

                if path not in method_map:
                    method_map[path] = {}

                # Derive an OperationId from the method name data
                operation_id = self._construct_operation_id(
                    service.__name__, protorpc_meth_name)

                method_map[path][verb] = self.__method_descriptor(
                    service, method_info, operation_id, protorpc_meth_info,
                    security_definitions)

                # Make sure the same method name isn't repeated.
                if method_id in method_collision_tracker:
                    raise api_exceptions.ApiConfigurationError(
                        'Method %s used multiple times, in classes %s and %s' %
                        (method_id, method_collision_tracker[method_id],
                         service.__name__))
                else:
                    method_collision_tracker[method_id] = service.__name__

                # Make sure the same HTTP method & path aren't repeated.
                rest_identifier = (method_info.http_method,
                                   method_info.get_path(service.api_info))
                if rest_identifier in rest_collision_tracker:
                    raise api_exceptions.ApiConfigurationError(
                        '%s path "%s" used multiple times, in classes %s and %s'
                        % (method_info.http_method,
                           method_info.get_path(service.api_info),
                           rest_collision_tracker[rest_identifier],
                           service.__name__))
                else:
                    rest_collision_tracker[rest_identifier] = service.__name__

        if method_map:
            descriptor['paths'] = method_map

        # Add request and/or response definitions, if any
        definitions = self.__definitions_descriptor()
        if definitions:
            descriptor['definitions'] = definitions

        descriptor['securityDefinitions'] = security_definitions

        return descriptor