#!/usr/bin/python
#
# Copyright (C) 2009 Google Inc.
#
# 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.


"""Provides functions to persist serialized auth tokens in the datastore.

The get_token and set_token functions should be used in conjunction with
gdata.gauth's token_from_blob and token_to_blob to allow auth token objects
to be reused across requests. It is up to your own code to ensure that the
token key's are unique.
"""

__author__ = 'j.s@google.com (Jeff Scudder)'


from google.appengine.ext import db
from google.appengine.api import memcache


class Token(db.Model):
  """Datastore Model which stores a serialized auth token."""
  t = db.BlobProperty()


def get_token(unique_key):
  """Searches for a stored token with the desired key.

  Checks memcache and then the datastore if required.

  Args:
    unique_key: str which uniquely identifies the desired auth token.

  Returns:
    A string encoding the auth token data. Use gdata.gauth.token_from_blob to
    convert back into a usable token object. None if the token was not found
    in memcache or the datastore.
  """
  token_string = memcache.get(unique_key)
  if token_string is None:
    # The token wasn't in memcache, so look in the datastore.
    token = Token.get_by_key_name(unique_key)
    if token is None:
      return None
    return token.t
  return token_string


def set_token(unique_key, token_str):
  """Saves the serialized auth token in the datastore.

  The token is also stored in memcache to speed up retrieval on a cache hit.

  Args:
    unique_key: The unique name for this token as a string. It is up to your
        code to ensure that this token value is unique in your application.
        Previous values will be silently overwitten.
    token_str: A serialized auth token as a string. I expect that this string
        will be generated by gdata.gauth.token_to_blob.

  Returns:
    True if the token was stored sucessfully, False if the token could not be
    safely cached (if an old value could not be cleared). If the token was
    set in memcache, but not in the datastore, this function will return None.
    However, in that situation an exception will likely be raised.

  Raises:
    Datastore exceptions may be raised from the App Engine SDK in the event of
    failure.
  """
  # First try to save in memcache.
  result = memcache.set(unique_key, token_str)
  # If memcache fails to save the value, clear the cached value.
  if not result:
    result = memcache.delete(unique_key)
    # If we could not clear the cached value for this token, refuse to save.
    if result == 0:
      return False
  # Save to the datastore.
  if Token(key_name=unique_key, t=token_str).put():
    return True
  return None


def delete_token(unique_key):
  # Clear from memcache.
  memcache.delete(unique_key)
  # Clear from the datastore.
  Token(key_name=unique_key).delete()
