class Index(validation.Validated):
    """Individual index definition.

  Order of the properties determines a given index's sort priority.

  Attributes:
    kind: Datastore kind that index belongs to.
    ancestors: Include ancestors in index.
    properties: Properties to be included.
  """

    ATTRIBUTES = {
        'kind': validation.Type(str, convert=False),
        'ancestor': validation.Type(bool, convert=False, default=False),
        'properties': validation.Optional(validation.Repeated(Property)),
    }

    def CheckInitialized(self):
        self._Normalize()
        super(Index, self).CheckInitialized()

    def _Normalize(self):
        if self.properties is None:
            return
        is_geo = any(x.mode == 'geospatial' for x in self.properties)
        for p in self.properties:
            if is_geo:
                if p.direction is not None:
                    raise validation.ValidationError(
                        'direction not supported in a geospatial index')
            else:

                if p.IsAscending():
                    p.direction = 'asc'
Beispiel #2
0
class PetStore(v.Validated):
    """Each test document maps to a PetStore."""
    ATTRIBUTES = {
        'name': v.Type(str),
        'address': v.Optional(str),
        'pets': v.Optional(v.Repeated(Pet)),
        'mascot': v.Optional(v.Type(Pet, False)),
        'phones': v.Optional(PhoneNumberDict),
    }
class PropertyEntry(validation.Validated):
    """Describes the transform for a single property."""

    ATTRIBUTES = {
        'property': validation.Type(str),
        'import_transform': OPTIONAL_EVALUATED_CALLABLE,
        'import_template': validation.Optional(validation.TYPE_STR),
        'default_value': validation.Optional(validation.TYPE_STR),
        'export': validation.Optional(validation.Repeated(ExportEntry)),
    }
    ATTRIBUTES.update(ExportEntry.ATTRIBUTES)

    def CheckInitialized(self):
        """Check that all required (combinations) of fields are set.

    Also fills in computed properties.

    Raises:
      InvalidConfiguration: If the config is invalid.
    """
        super(PropertyEntry, self).CheckInitialized()

        if not (self.external_name or self.import_template or self.export):
            raise bulkloader_errors.InvalidConfiguration(
                'Neither external_name nor import_template nor export specified for '
                'property %s.' % self.property)
class Property(validation.Validated):
    """Representation for an individual property of an index.

  Attributes:
    name: Name of attribute to sort by.
    direction: Direction of sort.
    mode: How the property is indexed. Either 'geospatial'
        or None (unspecified).
  """

    ATTRIBUTES = {
        'name':
        validation.Type(str, convert=False),
        'direction':
        validation.Optional([('asc', ('ascending', )),
                             ('desc', ('descending', ))]),
        'mode':
        validation.Optional(['geospatial'])
    }

    def IsAscending(self):

        return self.direction != 'desc'

    def CheckInitialized(self):
        if self.direction is not None and self.mode is not None:
            raise validation.ValidationError(
                'direction and mode are mutually exclusive')
        super(Property, self).CheckInitialized()
Beispiel #5
0
class Index(validation.Validated):
  """Individual index definition.

  Order of the properties determines a given indexes sort priority.

  Attributes:
    kind: Datastore kind that index belongs to.
    ancestors: Include ancestors in index.
    properties: Properties to sort on.
  """

  ATTRIBUTES = {
      'kind': validation.Type(str, convert=False),
      'ancestor': validation.Type(bool, convert=False, default=False),
      'properties': validation.Optional(validation.Repeated(Property)),
  }
Beispiel #6
0
class Pet(v.Validated):
    """Pet is used to test repeated fields."""
    ATTRIBUTES = {
        'name': v.Type(str),
        'age': v.Optional(int),
        'color': v.Optional(['black', 'brown', 'white']),
    }
Beispiel #7
0
class Library(validation.Validated):
  """Class representing the configuration of a single library."""

  ATTRIBUTES = {'name': validation.Type(str),
                'version': validation.Type(str)}

  def CheckInitialized(self):
    """Raises if the library configuration is not valid."""
    super(Library, self).CheckInitialized()
    if self.name not in SUPPORTED_LIBRARIES:
      raise appinfo_errors.InvalidLibraryName(
          'the library "%s" is not supported' % self.name)
    if self.version != 'latest':
      if self.version not in SUPPORTED_LIBRARIES[self.name]:
        raise appinfo_errors.InvalidLibraryVersion(
            '%s version "%s" is not supported, '
            'use one of: "%s" or "latest"' % (
                self.name,
                self.version,
                '", "'.join(SUPPORTED_LIBRARIES[self.name])))
Beispiel #8
0
class Property(validation.Validated):
  """Representation for an individual property of an index.

  Attributes:
    name: Name of attribute to sort by.
    direction: Direction of sort.
  """

  ATTRIBUTES = {
      'name': validation.Type(str, convert=False),
      'direction': validation.Options(('asc', ('ascending',)),
                                      ('desc', ('descending',)),
                                      default='asc'),
      }
class Property(validation.Validated):
    """Representation for an individual property of an index.

  This class must be kept in sync with
  java/com/google/apphosting/utils/config/IndexYamlReader.java.

  Attributes:
    name: Name of attribute to sort by.
    direction: Direction of sort.
  """

    ATTRIBUTES = {
        'name':
        validation.Type(str, convert=False),
        'direction':
        validation.Options(('asc', ('ascending', )),
                           ('desc', ('descending', )),
                           default='asc'),
    }
Beispiel #10
0
class Property(validation.Validated):
    """Representation for an individual property of an index.

  This class must be kept in sync with
  java/com/google/apphosting/utils/config/IndexYamlReader.java.

  Attributes:
    name: Name of attribute to sort by.
    direction: Direction of sort.
    mode: How the property is indexed. Either 'geospatial', 'segment'
        or None (unspecified).
  """

    ATTRIBUTES = {
        'name':
        validation.Type(str, convert=False),
        'direction':
        validation.Options(('asc', ('ascending', )),
                           ('desc', ('descending', )),
                           default='asc'),
        'mode':
        validation.Optional(['geospatial', 'segment']),
    }

    def __init__(self, **attributes):

        object.__setattr__(self, '_is_set', set([]))
        super(Property, self).__init__(**attributes)

    def __setattr__(self, key, value):
        self._is_set.add(key)
        super(Property, self).__setattr__(key, value)

    def IsAscending(self):
        return self.direction != 'desc'

    def CheckInitialized(self):
        if ('direction' in self._is_set
                and self.mode in ['geospatial', 'segment']):
            raise validation.ValidationError('Direction on a %s-mode '
                                             'property is not allowed.' %
                                             self.mode)
Beispiel #11
0
 class OneLevel(validation.Validated):
   ATTRIBUTES = {
       'key1': validation.Type(str),
       'key2': validation.Type(int),
   }
Beispiel #12
0
 class ValidatedInner(validation.Validated):
   ATTRIBUTES = {'name': validation.Type(str), 'job': validation.Type(str)}
Beispiel #13
0
class AppInfoExternal(validation.Validated):
    """Class representing users application info.

  This class is passed to a yaml_object builder to provide the validation
  for the application information file format parser.

  Attributes:
    application: Unique identifier for application.
    version: Application's major version.
    runtime: Runtime used by application.
    api_version: Which version of APIs to use.
    handlers: List of URL handlers.
    default_expiration: Default time delta to use for cache expiration for
      all static files, unless they have their own specific 'expiration' set.
      See the URLMap.expiration field's documentation for more information.
    skip_files: An re object.  Files that match this regular expression will
      not be uploaded by appcfg.py.  For example:
        skip_files: |
          .svn.*|
          #.*#
    nobuild_files: An re object.  Files that match this regular expression will
      not be built into the app.  Go only.
    api_config: URL root and script/servlet path for enhanced api serving
  """

    ATTRIBUTES = {
        APPLICATION:
        APPLICATION_RE_STRING,
        VERSION:
        validation.Optional(VERSION_RE_STRING),
        RUNTIME:
        RUNTIME_RE_STRING,
        API_VERSION:
        API_VERSION_RE_STRING,
        BUILTINS:
        validation.Optional(validation.Repeated(BuiltinHandler)),
        INCLUDES:
        validation.Optional(validation.Type(list)),
        HANDLERS:
        validation.Optional(validation.Repeated(URLMap)),
        LIBRARIES:
        validation.Optional(validation.Repeated(Library)),
        SERVICES:
        validation.Optional(
            validation.Repeated(validation.Regex(_SERVICE_RE_STRING))),
        DEFAULT_EXPIRATION:
        validation.Optional(_EXPIRATION_REGEX),
        SKIP_FILES:
        validation.RegexStr(default=DEFAULT_SKIP_FILES),
        NOBUILD_FILES:
        validation.RegexStr(default=DEFAULT_NOBUILD_FILES),
        DERIVED_FILE_TYPE:
        validation.Optional(
            validation.Repeated(
                validation.Options(JAVA_PRECOMPILED, PYTHON_PRECOMPILED))),
        ADMIN_CONSOLE:
        validation.Optional(AdminConsole),
        ERROR_HANDLERS:
        validation.Optional(validation.Repeated(ErrorHandlers)),
        BACKENDS:
        validation.Optional(validation.Repeated(backendinfo.BackendEntry)),
        THREADSAFE:
        validation.Optional(bool),
        API_CONFIG:
        validation.Optional(ApiConfigHandler),
    }

    def CheckInitialized(self):
        """Performs non-regex-based validation.

    The following are verified:
      - At least one url mapping is provided in the URL mappers.
      - Number of url mappers doesn't exceed MAX_URL_MAPS.
      - Major version does not contain the string -dot-.
      - If api_endpoints are defined, an api_config stanza must be defined.
      - If the runtime is python27 and threadsafe is set, then no CGI handlers
        can be used.
      - That the version name doesn't start with BUILTIN_NAME_PREFIX

    Raises:
      MissingURLMapping: if no URLMap object is present in the object.
      TooManyURLMappings: if there are too many URLMap entries.
      MissingApiConfig: if api_endpoints exist without an api_config.
      ThreadsafeWithCgiHandler: if the runtime is python27, threadsafe is set
          and CGI handlers are specified.
    """
        super(AppInfoExternal, self).CheckInitialized()
        if not self.handlers and not self.builtins and not self.includes:
            raise appinfo_errors.MissingURLMapping(
                'No URLMap entries found in application configuration')
        if self.handlers and len(self.handlers) > MAX_URL_MAPS:
            raise appinfo_errors.TooManyURLMappings(
                'Found more than %d URLMap entries in application configuration'
                % MAX_URL_MAPS)

        if self.libraries:
            if self.runtime != 'python27':
                raise appinfo_errors.RuntimeDoesNotSupportLibraries(
                    'libraries entries are only supported by the "python27" runtime'
                )

            library_names = [library.name for library in self.libraries]
            for library_name in library_names:
                if library_names.count(library_name) > 1:
                    raise appinfo_errors.DuplicateLibrary(
                        'Duplicate library entry for %s' % library_name)

        if self.version and self.version.find(
                ALTERNATE_HOSTNAME_SEPARATOR) != -1:
            raise validation.ValidationError(
                'Version "%s" cannot contain the string "%s"' %
                (self.version, ALTERNATE_HOSTNAME_SEPARATOR))
        if self.version and self.version.startswith(BUILTIN_NAME_PREFIX):
            raise validation.ValidationError(
                ('Version "%s" cannot start with "%s" because it is a '
                 'reserved version name prefix.') %
                (self.version, BUILTIN_NAME_PREFIX))
        if self.handlers:
            api_endpoints = [
                handler.url for handler in self.handlers
                if handler.GetHandlerType() == HANDLER_API_ENDPOINT
            ]
            if api_endpoints and not self.api_config:
                raise appinfo_errors.MissingApiConfig(
                    'An api_endpoint handler was specified, but the required '
                    'api_config stanza was not configured.')
            if self.threadsafe and self.runtime == 'python27':
                for handler in self.handlers:
                    if (handler.script and (handler.script.endswith('.py')
                                            or '/' in handler.script)):
                        raise appinfo_errors.ThreadsafeWithCgiHandler(
                            'Threadsafe cannot be enabled with CGI handler: %s'
                            % handler.script)

    def ApplyBackendSettings(self, backend_name):
        """Applies settings from the indicated backend to the AppInfoExternal.

    Backend entries may contain directives that modify other parts of the
    app.yaml, such as the 'start' directive, which adds a handler for the start
    request.  This method performs those modifications.

    Args:
      backend_name: The name of a backend defined in 'backends'.

    Raises:
      BackendNotFound: If the indicated backend was not listed in 'backends'.
    """
        if backend_name is None:
            return

        if self.backends is None:
            raise appinfo_errors.BackendNotFound

        self.version = backend_name

        match = None
        for backend in self.backends:
            if backend.name != backend_name:
                continue
            if match:
                raise appinfo_errors.DuplicateBackend
            else:
                match = backend

        if match is None:
            raise appinfo_errors.BackendNotFound

        if match.start is None:
            return

        start_handler = URLMap(url=_START_PATH, script=match.start)
        self.handlers.insert(0, start_handler)
Beispiel #14
0
 class Typed(validation.Validated):
   ATTRIBUTES = {
       'astring': validation.Type(str),
       'alist': validation.Type(list),
       'anint': validation.Type(int, False)
   }
Beispiel #15
0
 class HasDefault(validation.Validated):
   ATTRIBUTES = {'a': validation.Type(str, default='a string')}
Beispiel #16
0
 class HasDefaults(validation.Validated):
   ATTRIBUTES = {
       'prop1': validation.Type(str, default='required default'),
       'prop2': validation.Optional(str),
       'prop3': validation.Optional(str, default='optional default'),
   }
Beispiel #17
0
 class CatStore(validation.Validated):
   ATTRIBUTES = {
       'name': validation.Type(str),
       'address': validation.Optional(str),
       'cats': validation.Repeated(Cat),
   }
Beispiel #18
0
 class Cat(validation.Validated):
   ATTRIBUTES = {
       'name': validation.Type(str),
       'age': int,
       'whiskers': validation.Optional(int),
   }
Beispiel #19
0
 class HasOptional(v.Validated):
     ATTRIBUTES = {
         'required': v.Type(int),
         'optional': v.Optional(v.Type(int)),
     }
class AppInclude(validation.Validated):
    """Class representing the contents of an included app.yaml file.

  Used for both builtins and includes directives.
  """

    ATTRIBUTES = {
        BUILTINS: validation.Optional(validation.Repeated(BuiltinHandler)),
        INCLUDES: validation.Optional(validation.Type(list)),
        HANDLERS: validation.Optional(validation.Repeated(URLMap)),
        ADMIN_CONSOLE: validation.Optional(AdminConsole),
    }

    @classmethod
    def MergeAppYamlAppInclude(cls, appyaml, appinclude):
        """This function merges an app.yaml file with referenced builtins/includes.
    """

        if not appinclude:
            return appyaml

        if appinclude.handlers:
            tail = appyaml.handlers or []
            appyaml.handlers = []

            for h in appinclude.handlers:
                if not h.position or h.position == 'head':
                    appyaml.handlers.append(h)
                else:
                    tail.append(h)

            appyaml.handlers.extend(tail)

        appyaml.admin_console = AdminConsole.Merge(appyaml.admin_console,
                                                   appinclude.admin_console)

        return appyaml

    @classmethod
    def MergeAppIncludes(cls, appinclude_one, appinclude_two):
        """This function merges the non-referential state of the provided AppInclude
    objects.  That is, builtins and includes directives are not preserved, but
    any static objects are copied into an aggregate AppInclude object that
    preserves the directives of both provided AppInclude objects.

    Args:
      appinclude_one: object one to merge
      appinclude_two: object two to merge

    Returns:
      AppInclude object that is the result of merging the static directives of
      appinclude_one and appinclude_two.
    """

        if not appinclude_one or not appinclude_two:
            return appinclude_one or appinclude_two

        if appinclude_one.handlers:
            if appinclude_two.handlers:
                appinclude_one.handlers.extend(appinclude_two.handlers)
        else:
            appinclude_one.handlers = appinclude_two.handlers

        appinclude_one.admin_console = (AdminConsole.Merge(
            appinclude_one.admin_console, appinclude_two.admin_console))

        return appinclude_one
Beispiel #21
0
 def GetValidator(self, key):
   if six.ensure_str(key).startswith('employee'):
     return validation.Type(ValidatedInner)
   return validation.Type(str)
class AppInfoExternal(validation.Validated):
    """Class representing users application info.

  This class is passed to a yaml_object builder to provide the validation
  for the application information file format parser.

  Attributes:
    application: Unique identifier for application.
    version: Application's major version number.
    runtime: Runtime used by application.
    api_version: Which version of APIs to use.
    handlers: List of URL handlers.
    default_expiration: Default time delta to use for cache expiration for
      all static files, unless they have their own specific 'expiration' set.
      See the URLMap.expiration field's documentation for more information.
    skip_files: An re object.  Files that match this regular expression will
      not be uploaded by appcfg.py.  For example:
        skip_files: |
          .svn.*|
          #.*#
  """

    ATTRIBUTES = {
        APPLICATION:
        APPLICATION_RE_STRING,
        VERSION:
        VERSION_RE_STRING,
        RUNTIME:
        RUNTIME_RE_STRING,
        API_VERSION:
        API_VERSION_RE_STRING,
        BUILTINS:
        validation.Optional(validation.Repeated(BuiltinHandler)),
        INCLUDES:
        validation.Optional(validation.Type(list)),
        HANDLERS:
        validation.Optional(validation.Repeated(URLMap)),
        SERVICES:
        validation.Optional(
            validation.Repeated(validation.Regex(_SERVICE_RE_STRING))),
        DEFAULT_EXPIRATION:
        validation.Optional(_EXPIRATION_REGEX),
        SKIP_FILES:
        validation.RegexStr(default=DEFAULT_SKIP_FILES),
        DERIVED_FILE_TYPE:
        validation.Optional(
            validation.Repeated(
                validation.Options(JAVA_PRECOMPILED, PYTHON_PRECOMPILED))),
        ADMIN_CONSOLE:
        validation.Optional(AdminConsole),
        ERROR_HANDLERS:
        validation.Optional(validation.Repeated(ErrorHandlers)),
    }

    def CheckInitialized(self):
        """Performs non-regex-based validation.

    Ensures that at least one url mapping is provided in the URL mappers
    Also ensures that the major version doesn't contain the string
    -dot-.

    Raises:
      MissingURLMapping when no URLMap objects are present in object.
      TooManyURLMappings when there are too many URLMap entries.
    """
        super(AppInfoExternal, self).CheckInitialized()
        if not self.handlers and not self.builtins and not self.includes:
            raise appinfo_errors.MissingURLMapping(
                'No URLMap entries found in application configuration')
        if self.handlers and len(self.handlers) > MAX_URL_MAPS:
            raise appinfo_errors.TooManyURLMappings(
                'Found more than %d URLMap entries in application configuration'
                % MAX_URL_MAPS)
        if self.version.find(ALTERNATE_HOSTNAME_SEPARATOR) != -1:
            raise validation.ValidationError(
                'App version "%s" cannot contain the string "%s"' %
                (self.version, ALTERNATE_HOSTNAME_SEPARATOR))
Beispiel #23
0
 class HasNested(validation.Validated):
   ATTRIBUTES = {
       'key1': validation.Type(str),
       'key2': validation.Type(Nested),
   }
Beispiel #24
0
 class Nested(validation.Validated):
   ATTRIBUTES = {'key1': validation.Type(int)}
Beispiel #25
0
 class HasType(v.Validated):
     ATTRIBUTES = {
         'prop_int': v.Type(int),
         'prop_string': v.Type(str),
     }