Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #170: handle namespaces in queries #255

Merged
merged 4 commits into from
Oct 19, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 19 additions & 3 deletions gcloud/datastore/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ class Query(object):

:type dataset: :class:`gcloud.datastore.dataset.Dataset`
:param dataset: The dataset to query.

:type namespace: string or None
:param dataset: The namespace to which to restrict results.
"""

OPERATORS = {
Expand All @@ -52,8 +55,9 @@ class Query(object):
}
"""Mapping of operator strings and their protobuf equivalents."""

def __init__(self, kind=None, dataset=None):
def __init__(self, kind=None, dataset=None, namespace=None):
self._dataset = dataset
self._namespace = namespace
self._pb = datastore_pb.Query()
self._cursor = None

Expand All @@ -66,11 +70,20 @@ def _clone(self):
:rtype: :class:`gcloud.datastore.query.Query`
:returns: a copy of 'self'.
"""
clone = self.__class__(dataset=self._dataset)
clone = self.__class__(dataset=self._dataset,
namespace=self._namespace)
clone._pb.CopyFrom(self._pb)
clone._cursor = self._cursor
return clone

def namespace(self):

This comment was marked as spam.

This comment was marked as spam.

"""This query's namespace

:rtype: string or None
:returns: the namespace assigned to this query
"""
return self._namespace

def to_protobuf(self):
"""Convert :class:`Query` instance to :class:`.datastore_v1_pb2.Query`.

Expand Down Expand Up @@ -316,7 +329,10 @@ def fetch(self, limit=None):
clone = self.limit(limit)

query_results = self.dataset().connection().run_query(
query_pb=clone.to_protobuf(), dataset_id=self.dataset().id())
query_pb=clone.to_protobuf(),
dataset_id=self.dataset().id(),
namespace=self._namespace,
)
entity_pbs, end_cursor = query_results[:2]

self._cursor = end_cursor
Expand Down
30 changes: 19 additions & 11 deletions gcloud/datastore/test_query.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,39 +8,44 @@ def _getTargetClass(self):

return Query

def _makeOne(self, kind=None, dataset=None):
return self._getTargetClass()(kind, dataset)
def _makeOne(self, kind=None, dataset=None, namespace=None):
return self._getTargetClass()(kind, dataset, namespace)

def test_ctor_defaults(self):
query = self._makeOne()
query = self._getTargetClass()()
self.assertEqual(query.dataset(), None)
self.assertEqual(list(query.kind()), [])
self.assertEqual(query.limit(), 0)
self.assertEqual(query.namespace(), None)

def test_ctor_explicit(self):
from gcloud.datastore.dataset import Dataset

_DATASET = 'DATASET'
_KIND = 'KIND'
_NAMESPACE = 'NAMESPACE'
dataset = Dataset(_DATASET)
query = self._makeOne(_KIND, dataset)
query = self._makeOne(_KIND, dataset, _NAMESPACE)
self.assertTrue(query.dataset() is dataset)
kq_pb, = list(query.kind())
self.assertEqual(kq_pb.name, _KIND)
self.assertEqual(query.namespace(), _NAMESPACE)

def test__clone(self):
from gcloud.datastore.dataset import Dataset

_DATASET = 'DATASET'
_KIND = 'KIND'
_CURSOR = 'DEADBEEF'
_NAMESPACE = 'NAMESPACE'
dataset = Dataset(_DATASET)
query = self._makeOne(_KIND, dataset)
query = self._makeOne(_KIND, dataset, _NAMESPACE)
query._cursor = _CURSOR
clone = query._clone()
self.assertFalse(clone is query)
self.assertTrue(isinstance(clone, self._getTargetClass()))
self.assertTrue(clone.dataset() is dataset)
self.assertEqual(clone.namespace(), _NAMESPACE)
kq_pb, = list(clone.kind())
self.assertEqual(kq_pb.name, _KIND)
self.assertEqual(clone._cursor, _CURSOR)
Expand Down Expand Up @@ -101,7 +106,7 @@ def test_ancestor_w_non_key_non_list(self):
query = self._makeOne()
self.assertRaises(TypeError, query.ancestor, object())

def test_ancester_wo_existing_ancestor_query_w_key_and_propfilter(self):
def test_ancestor_wo_existing_ancestor_query_w_key_and_propfilter(self):
from gcloud.datastore.key import Key
_KIND = 'KIND'
_ID = 123
Expand All @@ -121,7 +126,7 @@ def test_ancester_wo_existing_ancestor_query_w_key_and_propfilter(self):
self.assertEqual(p_pb.property.name, '__key__')
self.assertEqual(p_pb.value.key_value, key.to_protobuf())

def test_ancester_wo_existing_ancestor_query_w_key(self):
def test_ancestor_wo_existing_ancestor_query_w_key(self):
from gcloud.datastore.key import Key
_KIND = 'KIND'
_ID = 123
Expand All @@ -137,7 +142,7 @@ def test_ancester_wo_existing_ancestor_query_w_key(self):
self.assertEqual(p_pb.property.name, '__key__')
self.assertEqual(p_pb.value.key_value, key.to_protobuf())

def test_ancester_wo_existing_ancestor_query_w_list(self):
def test_ancestor_wo_existing_ancestor_query_w_list(self):
from gcloud.datastore.key import Key
_KIND = 'KIND'
_ID = 123
Expand All @@ -153,7 +158,7 @@ def test_ancester_wo_existing_ancestor_query_w_list(self):
self.assertEqual(p_pb.property.name, '__key__')
self.assertEqual(p_pb.value.key_value, key.to_protobuf())

def test_ancester_clears_existing_ancestor_query_w_only(self):
def test_ancestor_clears_existing_ancestor_query_w_only(self):
_KIND = 'KIND'
_ID = 123
query = self._makeOne()
Expand All @@ -164,7 +169,7 @@ def test_ancester_clears_existing_ancestor_query_w_only(self):
q_pb = after.to_protobuf()
self.assertEqual(list(q_pb.filter.composite_filter.filter), [])

def test_ancester_clears_existing_ancestor_query_w_others(self):
def test_ancestor_clears_existing_ancestor_query_w_others(self):
_KIND = 'KIND'
_ID = 123
_NAME = 'NAME'
Expand Down Expand Up @@ -257,6 +262,7 @@ def test_fetch_default_limit(self):
expected_called_with = {
'dataset_id': _DATASET,
'query_pb': query.to_protobuf(),
'namespace': None,
}
self.assertEqual(connection._called_with, expected_called_with)

Expand All @@ -266,6 +272,7 @@ def test_fetch_explicit_limit(self):
_DATASET = 'DATASET'
_KIND = 'KIND'
_ID = 123
_NAMESPACE = 'NAMESPACE'
entity_pb = Entity()
path_element = entity_pb.key.path_element.add()
path_element.kind = _KIND
Expand All @@ -276,7 +283,7 @@ def test_fetch_explicit_limit(self):
connection = _Connection(entity_pb)
connection._cursor = _CURSOR
dataset = _Dataset(_DATASET, connection)
query = self._makeOne(_KIND, dataset)
query = self._makeOne(_KIND, dataset, _NAMESPACE)
limited = query.limit(13)
entities = query.fetch(13)
self.assertEqual(query._cursor, _CURSOR)
Expand All @@ -286,6 +293,7 @@ def test_fetch_explicit_limit(self):
expected_called_with = {
'dataset_id': _DATASET,
'query_pb': limited.to_protobuf(),
'namespace': _NAMESPACE,
}
self.assertEqual(connection._called_with, expected_called_with)

Expand Down