#
# Copyright 2009 Google LLC. All Rights Reserved.
#
# 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.

"""Control the namespacing system used by various APIs.

A namespace may be specified in various API calls exemplified
by the datastore and memcache interfaces.  The default can be
specified using this module.
"""

# WARNING: This file is externally viewable by our users.  All comments from
# this file will be stripped.  The docstrings will NOT.  Do not put sensitive
# information in docstrings.  If you must communicate internal information in
# this source file, please place them in comments only.



import os
import re
import warnings

from googlecloudsdk.third_party.appengine.api import lib_config

__all__ = ['BadValueError',
           'set_namespace',
           'get_namespace',
           'google_apps_namespace',
           'enable_request_namespace',
           'validate_namespace',
          ]


# The name of an environment variable specifying the default namespace
# to use for various services including memcache and datastore.
_ENV_DEFAULT_NAMESPACE = 'HTTP_X_APPENGINE_DEFAULT_NAMESPACE'
_ENV_CURRENT_NAMESPACE = 'HTTP_X_APPENGINE_CURRENT_NAMESPACE'

# Namespace string valid pattern matcher. Any sequence of
# the characters 0-9, A-Z, a-z, '.', '_' or '-'.
# Note: Keep this consistent with the java NamespaceManager
# constant of the same name and in appid_namespace.
#
_NAMESPACE_MAX_LENGTH = 100
_NAMESPACE_PATTERN = r'^[0-9A-Za-z._-]{0,%s}$' % _NAMESPACE_MAX_LENGTH
_NAMESPACE_RE = re.compile(_NAMESPACE_PATTERN)

class _ConfigDefaults(object):
  def default_namespace_for_request():
      return None

_config = lib_config.register('namespace_manager_', _ConfigDefaults.__dict__)

def set_namespace(namespace):
  """Set the default namespace for the current HTTP request.

  Args:
    namespace: A string naming the new namespace to use. A value of None
      will unset the default namespace value.
  """
  if namespace is None:
    os.environ.pop(_ENV_CURRENT_NAMESPACE, None)
  else:
    validate_namespace(namespace)
    os.environ[_ENV_CURRENT_NAMESPACE] = namespace


def get_namespace():
  """Get the current default namespace or ('') namespace if unset."""
  name = os.environ.get(_ENV_CURRENT_NAMESPACE, None)
  if name is None:
    name = _config.default_namespace_for_request()
    if name is not None:
      set_namespace(name)
  if name is None:
    name = ''
  return name


def google_apps_namespace():
  return os.environ.get(_ENV_DEFAULT_NAMESPACE, None)

def enable_request_namespace():
  """Set the default namespace to the Google Apps domain referring this request.

  This method is deprecated, use lib_config instead.

  Calling this function will set the default namespace to the
  Google Apps domain that was used to create the url used for this request
  and only for the current request and only if the current default namespace
  is unset.

  """
  warnings.warn('namespace_manager.enable_request_namespace() is deprecated: '
                'use lib_config instead.',
                DeprecationWarning,
                stacklevel=2)
  if _ENV_CURRENT_NAMESPACE not in os.environ:
    if _ENV_DEFAULT_NAMESPACE in os.environ:
      os.environ[_ENV_CURRENT_NAMESPACE] = os.environ[_ENV_DEFAULT_NAMESPACE]


class BadValueError(Exception):
  """Raised by ValidateNamespaceString."""


def validate_namespace(value, exception=BadValueError):
  """Raises an exception if value is not a valid Namespace string.

  A namespace must match the regular expression ([0-9A-Za-z._-]{0,100}).

  Args:
    value: string, the value to validate.
    exception: exception type to raise.
  """
  if not isinstance(value, basestring):
    raise exception('value should be a string; received %r (a %s):' %
                    (value, type(value)))
  if not _NAMESPACE_RE.match(value):
    raise exception('value "%s" does not match regex "%s"' %
                    (value, _NAMESPACE_PATTERN))
