Skip to content

Commit

Permalink
Merge pull request #1308 from dhermes/bigtable-list-tables
Browse files Browse the repository at this point in the history
Implementing Bigtable list_column_families().
  • Loading branch information
dhermes committed Dec 21, 2015
2 parents 0870be4 + 8b01ef1 commit cf82bac
Show file tree
Hide file tree
Showing 4 changed files with 130 additions and 3 deletions.
18 changes: 18 additions & 0 deletions gcloud/bigtable/column_family.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,24 @@ def __init__(self, column_family_id, table, gc_rule=None):
self._table = table
self.gc_rule = gc_rule

@property
def name(self):
"""Column family name used in requests.
.. note::
This property will not change if ``column_family_id`` does not, but
the return value is not cached.
The table name is of the form
``"projects/../zones/../clusters/../tables/../columnFamilies/.."``
:rtype: str
:returns: The column family name.
"""
return self._table.name + '/columnFamilies/' + self.column_family_id

def __eq__(self, other):
if not isinstance(other, self.__class__):
return False
Expand Down
37 changes: 35 additions & 2 deletions gcloud/bigtable/table.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
bigtable_table_service_messages_pb2 as messages_pb2)
from gcloud.bigtable._generated import (
bigtable_service_messages_pb2 as data_messages_pb2)
from gcloud.bigtable.column_family import _gc_rule_from_pb
from gcloud.bigtable.column_family import ColumnFamily
from gcloud.bigtable.row import Row

Expand Down Expand Up @@ -73,17 +74,21 @@ def name(self):
"""
return self._cluster.name + '/tables/' + self.table_id

def column_family(self, column_family_id):
def column_family(self, column_family_id, gc_rule=None):
"""Factory to create a column family associated with this table.
:type column_family_id: str
:param column_family_id: The ID of the column family. Must be of the
form ``[_a-zA-Z0-9][-_.a-zA-Z0-9]*``.
:type gc_rule: :class:`.column_family.GarbageCollectionRule`
:param gc_rule: (Optional) The garbage collection settings for this
column family.
:rtype: :class:`.column_family.ColumnFamily`
:returns: A column family owned by this table.
"""
return ColumnFamily(column_family_id, self)
return ColumnFamily(column_family_id, self, gc_rule=gc_rule)

def row(self, row_key):
"""Factory to create a row associated with this table.
Expand Down Expand Up @@ -179,6 +184,34 @@ def delete(self):
# We expect a `._generated.empty_pb2.Empty`
client._table_stub.DeleteTable(request_pb, client.timeout_seconds)

def list_column_families(self):
"""List the column families owned by this table.
:rtype: dictionary with string as keys and
:class:`.column_family.ColumnFamily` as values
:returns: List of column families attached to this table.
:raises: :class:`ValueError <exceptions.ValueError>` if the column
family name from the response does not agree with the computed
name from the column family ID.
"""
request_pb = messages_pb2.GetTableRequest(name=self.name)
client = self._cluster._client
# We expect a `._generated.bigtable_table_data_pb2.Table`
table_pb = client._table_stub.GetTable(request_pb,
client.timeout_seconds)

result = {}
for column_family_id, value_pb in table_pb.column_families.items():
gc_rule = _gc_rule_from_pb(value_pb.gc_rule)
column_family = self.column_family(column_family_id,
gc_rule=gc_rule)
if column_family.name != value_pb.name:
raise ValueError('Column family name %s does not agree with '
'name from request: %s.' % (
column_family.name, value_pb.name))
result[column_family_id] = column_family
return result

def sample_row_keys(self):
"""Read a sample of row keys in the table.
Expand Down
15 changes: 15 additions & 0 deletions gcloud/bigtable/test_column_family.py
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,15 @@ def test_constructor(self):
self.assertTrue(column_family._table is table)
self.assertTrue(column_family.gc_rule is gc_rule)

def test_name_property(self):
column_family_id = u'column-family-id'
table_name = 'table_name'
table = _Table(table_name)
column_family = self._makeOne(column_family_id, table)

expected_name = table_name + '/columnFamilies/' + column_family_id
self.assertEqual(column_family.name, expected_name)

def test___eq__(self):
column_family_id = 'column_family_id'
table = object()
Expand Down Expand Up @@ -460,3 +469,9 @@ def WhichOneof(cls, name):
self.assertEqual(MockProto.names, [])
self.assertRaises(ValueError, self._callFUT, MockProto)
self.assertEqual(MockProto.names, ['rule'])


class _Table(object):

def __init__(self, name):
self.name = name
63 changes: 62 additions & 1 deletion gcloud/bigtable/test_table.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,14 @@ def test_column_family_factory(self):
from gcloud.bigtable.column_family import ColumnFamily

table_id = 'table-id'
gc_rule = object()
table = self._makeOne(table_id, None)
column_family_id = 'column_family_id'
column_family = table.column_family(column_family_id)
column_family = table.column_family(column_family_id, gc_rule=gc_rule)

self.assertTrue(isinstance(column_family, ColumnFamily))
self.assertEqual(column_family.column_family_id, column_family_id)
self.assertTrue(column_family.gc_rule is gc_rule)
self.assertEqual(column_family._table, table)

def test_row_factory(self):
Expand Down Expand Up @@ -188,6 +190,65 @@ def test_rename(self):
{},
)])

def _list_column_families_helper(self, column_family_name=None):
from gcloud.bigtable._generated import (
bigtable_table_data_pb2 as data_pb2)
from gcloud.bigtable._generated import (
bigtable_table_service_messages_pb2 as messages_pb2)
from gcloud.bigtable._testing import _FakeStub

project_id = 'project-id'
zone = 'zone'
cluster_id = 'cluster-id'
table_id = 'table-id'
timeout_seconds = 502
cluster_name = ('projects/' + project_id + '/zones/' + zone +
'/clusters/' + cluster_id)

client = _Client(timeout_seconds=timeout_seconds)
cluster = _Cluster(cluster_name, client=client)
table = self._makeOne(table_id, cluster)

# Create request_pb
table_name = cluster_name + '/tables/' + table_id
request_pb = messages_pb2.GetTableRequest(name=table_name)

# Create response_pb
column_family_id = 'foo'
if column_family_name is None:
column_family_name = (table_name + '/columnFamilies/' +
column_family_id)
column_family = data_pb2.ColumnFamily(name=column_family_name)
response_pb = data_pb2.Table(
column_families={column_family_id: column_family},
)

# Patch the stub used by the API method.
client._table_stub = stub = _FakeStub(response_pb)

# Create expected_result.
expected_result = {
column_family_id: table.column_family(column_family_id),
}

# Perform the method and check the result.
result = table.list_column_families()
self.assertEqual(result, expected_result)
self.assertEqual(stub.method_calls, [(
'GetTable',
(request_pb, timeout_seconds),
{},
)])

def test_list_column_families(self):
self._list_column_families_helper()

def test_list_column_families_failure(self):
column_family_name = 'not-the-right-format'
with self.assertRaises(ValueError):
self._list_column_families_helper(
column_family_name=column_family_name)

def test_delete(self):
from gcloud.bigtable._generated import (
bigtable_table_service_messages_pb2 as messages_pb2)
Expand Down

0 comments on commit cf82bac

Please sign in to comment.