Skip to content

Commit

Permalink
Implementing Table.create_column_family().
Browse files Browse the repository at this point in the history
  • Loading branch information
dhermes committed Jul 28, 2015
1 parent 71299ed commit f3460ce
Show file tree
Hide file tree
Showing 4 changed files with 267 additions and 66 deletions.
83 changes: 82 additions & 1 deletion gcloud_bigtable/table.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ class GarbageCollectionRule(object):
Cells in the table fitting the rule will be deleted during
garbage collection.
These values can be combined via :class:`GarbageCollectionRuleUnion` and
:class:`GarbageCollectionRuleIntersection.`
.. note::
At most one of ``max_num_versions`` and ``max_age`` can be specified
Expand Down Expand Up @@ -71,7 +74,7 @@ def to_pb(self):
"""Converts the :class:`GarbageCollectionRule` to a protobuf.
:rtype: :class:`data_pb2.GcRule`
:returns: The convert current object.
:returns: The converted current object.
"""
gc_rule_kwargs = {}
if self.max_num_versions is not None:
Expand All @@ -81,6 +84,50 @@ def to_pb(self):
return data_pb2.GcRule(**gc_rule_kwargs)


class GarbageCollectionRuleUnion(object):
"""Union of garbage collection rules.
:type rules: list
:param rules: List of garbage collection rules, unions and/or
intersections.
"""

def __init__(self, rules=None):
self.rules = rules

def to_pb(self):
"""Converts the union into a single gc rule as a protobuf.
:rtype: :class:`data_pb2.GcRule`
:returns: The converted current object.
"""
union = data_pb2.GcRule.Union(
rules=[rule.to_pb() for rule in self.rules])
return data_pb2.GcRule(union=union)


class GarbageCollectionRuleIntersection(object):
"""Intersection of garbage collection rules.
:type rules: list
:param rules: List of garbage collection rules, unions and/or
intersections.
"""

def __init__(self, rules=None):
self.rules = rules

def to_pb(self):
"""Converts the intersection into a single gc rule as a protobuf.
:rtype: :class:`data_pb2.GcRule`
:returns: The converted current object.
"""
intersection = data_pb2.GcRule.Intersection(
rules=[rule.to_pb() for rule in self.rules])
return data_pb2.GcRule(intersection=intersection)


class Table(object):
"""Representation of a Google Cloud Bigtable Table.
Expand Down Expand Up @@ -263,3 +310,37 @@ def delete(self, timeout_seconds=TIMEOUT_SECONDS):
response = stub.DeleteTable.async(request_pb, timeout_seconds)
# We expect a `._generated.empty_pb2.Empty`
response.result()

def create_column_family(self, column_family_id, gc_rule=None,
timeout_seconds=TIMEOUT_SECONDS):
"""Create a column family in this table.
:type column_family_id: string
:param column_family_id: The ID of the column family.
:type gc_rule: :class:`GarbageCollectionRule`,
:class:`GarbageCollectionRuleUnion` or
:class:`GarbageCollectionRuleIntersection`
:param gc_rule: The garbage collection settings for the column family.
:type timeout_seconds: integer
:param timeout_seconds: Number of seconds for request time-out.
If not passed, defaults to ``TIMEOUT_SECONDS``.
"""
if gc_rule is None:
column_family = data_pb2.ColumnFamily()
else:
column_family = data_pb2.ColumnFamily(gc_rule=gc_rule.to_pb())
request_pb = messages_pb2.CreateColumnFamilyRequest(
name=self.name,
column_family_id=column_family_id,
column_family=column_family,
)

stub = make_stub(self.credentials, TABLE_STUB_FACTORY,
TABLE_ADMIN_HOST, TABLE_ADMIN_PORT)
with stub:
response = stub.CreateColumnFamily.async(request_pb,
timeout_seconds)
# We expect a `._generated.bigtable_table_data_pb2.ColumnFamily`
response.result()
49 changes: 0 additions & 49 deletions gcloud_bigtable/table_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,55 +39,6 @@ class TableConnection(Connection):
SCOPE = 'https://www.googleapis.com/auth/cloud-bigtable.admin'
"""Scope for Table Admin and Cluster Admin API requests."""

def create_column_family(self, cluster_name, table_id, column_family_id,
timeout_seconds=TIMEOUT_SECONDS):
"""Create a column family in a table.
.. note::
For now, we do not support passing in a custom column family in the
request. Aside from the ``column_family_id``, the fields being left
out are
* ``gc_expression``
* ``gc_rule``
:type cluster_name: string
:param cluster_name: The name of the cluster where the column family
will be created. Must be of the form
"projects/../zones/../clusters/.."
Since this is a low-level class, we don't check
this, rather we expect callers to pass correctly
formatted data.
:type table_id: string
:param table_id: The name of the table within the cluster.
:type column_family_id: string
:param column_family_id: The name of the column family within
the table.
:type timeout_seconds: integer
:param timeout_seconds: Number of seconds for request time-out.
If not passed, defaults to ``TIMEOUT_SECONDS``.
:rtype: :class:`._generated.bigtable_table_data_pb2.ColumnFamily`
:returns: The column family created.
"""
table_name = '%s/tables/%s' % (cluster_name, table_id)
request_pb = messages_pb2.CreateColumnFamilyRequest(
column_family_id=column_family_id,
name=table_name,
)
result_pb = None
stub = make_stub(self._credentials, TABLE_STUB_FACTORY,
TABLE_ADMIN_HOST, PORT)
with stub:
response = stub.CreateColumnFamily.async(request_pb,
timeout_seconds)
result_pb = response.result()

return result_pb

def update_column_family(self, cluster_name, table_id, column_family_id,
timeout_seconds=TIMEOUT_SECONDS):
"""Update an existing column family in a table.
Expand Down
185 changes: 185 additions & 0 deletions gcloud_bigtable/test_table.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,143 @@ def test_to_pb_with_max_age(self):
self.assertEqual(pb_val, data_pb2.GcRule(max_age=duration))


class TestGarbageCollectionRuleUnion(unittest2.TestCase):

def _getTargetClass(self):
from gcloud_bigtable.table import GarbageCollectionRuleUnion
return GarbageCollectionRuleUnion

def _makeOne(self, *args, **kwargs):
return self._getTargetClass()(*args, **kwargs)

def test_constructor(self):
rules = object()
rule_union = self._makeOne(rules=rules)
self.assertTrue(rule_union.rules is rules)

def test_to_pb(self):
import datetime
from gcloud_bigtable._generated import (
bigtable_table_data_pb2 as data_pb2)
from gcloud_bigtable._generated import duration_pb2
from gcloud_bigtable.table import GarbageCollectionRule

max_num_versions = 42
rule1 = GarbageCollectionRule(max_num_versions=max_num_versions)
pb_rule1 = data_pb2.GcRule(max_num_versions=max_num_versions)

max_age = datetime.timedelta(seconds=1)
rule2 = GarbageCollectionRule(max_age=max_age)
pb_rule2 = data_pb2.GcRule(max_age=duration_pb2.Duration(seconds=1))

rule3 = self._makeOne(rules=[rule1, rule2])
pb_rule3 = data_pb2.GcRule(
union=data_pb2.GcRule.Union(rules=[pb_rule1, pb_rule2]))

gc_rule_pb = rule3.to_pb()
self.assertEqual(gc_rule_pb, pb_rule3)

def test_to_pb_nested(self):
import datetime
from gcloud_bigtable._generated import (
bigtable_table_data_pb2 as data_pb2)
from gcloud_bigtable._generated import duration_pb2
from gcloud_bigtable.table import GarbageCollectionRule

max_num_versions1 = 42
rule1 = GarbageCollectionRule(max_num_versions=max_num_versions1)
pb_rule1 = data_pb2.GcRule(max_num_versions=max_num_versions1)

max_age = datetime.timedelta(seconds=1)
rule2 = GarbageCollectionRule(max_age=max_age)
pb_rule2 = data_pb2.GcRule(max_age=duration_pb2.Duration(seconds=1))

rule3 = self._makeOne(rules=[rule1, rule2])
pb_rule3 = data_pb2.GcRule(
union=data_pb2.GcRule.Union(rules=[pb_rule1, pb_rule2]))

max_num_versions2 = 1337
rule4 = GarbageCollectionRule(max_num_versions=max_num_versions2)
pb_rule4 = data_pb2.GcRule(max_num_versions=max_num_versions2)

rule5 = self._makeOne(rules=[rule3, rule4])
pb_rule5 = data_pb2.GcRule(
union=data_pb2.GcRule.Union(rules=[pb_rule3, pb_rule4]))

gc_rule_pb = rule5.to_pb()
self.assertEqual(gc_rule_pb, pb_rule5)


class TestGarbageCollectionRuleIntersection(unittest2.TestCase):

def _getTargetClass(self):
from gcloud_bigtable.table import GarbageCollectionRuleIntersection
return GarbageCollectionRuleIntersection

def _makeOne(self, *args, **kwargs):
return self._getTargetClass()(*args, **kwargs)

def test_constructor(self):
rules = object()
rule_intersection = self._makeOne(rules=rules)
self.assertTrue(rule_intersection.rules is rules)

def test_to_pb(self):
import datetime
from gcloud_bigtable._generated import (
bigtable_table_data_pb2 as data_pb2)
from gcloud_bigtable._generated import duration_pb2
from gcloud_bigtable.table import GarbageCollectionRule

max_num_versions = 42
rule1 = GarbageCollectionRule(max_num_versions=max_num_versions)
pb_rule1 = data_pb2.GcRule(max_num_versions=max_num_versions)

max_age = datetime.timedelta(seconds=1)
rule2 = GarbageCollectionRule(max_age=max_age)
pb_rule2 = data_pb2.GcRule(max_age=duration_pb2.Duration(seconds=1))

rule3 = self._makeOne(rules=[rule1, rule2])
pb_rule3 = data_pb2.GcRule(
intersection=data_pb2.GcRule.Intersection(
rules=[pb_rule1, pb_rule2]))

gc_rule_pb = rule3.to_pb()
self.assertEqual(gc_rule_pb, pb_rule3)

def test_to_pb_nested(self):
import datetime
from gcloud_bigtable._generated import (
bigtable_table_data_pb2 as data_pb2)
from gcloud_bigtable._generated import duration_pb2
from gcloud_bigtable.table import GarbageCollectionRule

max_num_versions1 = 42
rule1 = GarbageCollectionRule(max_num_versions=max_num_versions1)
pb_rule1 = data_pb2.GcRule(max_num_versions=max_num_versions1)

max_age = datetime.timedelta(seconds=1)
rule2 = GarbageCollectionRule(max_age=max_age)
pb_rule2 = data_pb2.GcRule(max_age=duration_pb2.Duration(seconds=1))

rule3 = self._makeOne(rules=[rule1, rule2])
pb_rule3 = data_pb2.GcRule(
intersection=data_pb2.GcRule.Intersection(
rules=[pb_rule1, pb_rule2]))

max_num_versions2 = 1337
rule4 = GarbageCollectionRule(max_num_versions=max_num_versions2)
pb_rule4 = data_pb2.GcRule(max_num_versions=max_num_versions2)

rule5 = self._makeOne(rules=[rule3, rule4])
pb_rule5 = data_pb2.GcRule(
intersection=data_pb2.GcRule.Intersection(
rules=[pb_rule3, pb_rule4]))

gc_rule_pb = rule5.to_pb()
self.assertEqual(gc_rule_pb, pb_rule5)


class TestTable(GRPCMockTestMixin):

@classmethod
Expand Down Expand Up @@ -275,6 +412,54 @@ def result_method(client):
request_pb, response_pb, expected_result,
PROJECT_ID)

def _create_column_family_test_helper(self, gc_rule=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)

# Create request_pb
column_family_id = 'column_family_id'
table_name = ('projects/' + PROJECT_ID + '/zones/' + ZONE +
'/clusters/' + CLUSTER_ID + '/tables/' + TABLE_ID)
if gc_rule is None:
column_family = data_pb2.ColumnFamily()
else:
column_family = data_pb2.ColumnFamily(gc_rule=gc_rule.to_pb())
request_pb = messages_pb2.CreateColumnFamilyRequest(
name=table_name,
column_family_id=column_family_id,
column_family=column_family,
)

# Create response_pb
response_pb = data_pb2.ColumnFamily()

# Create expected_result.
expected_result = None # create_column_family() has no return value.

# We must create the cluster with the client passed in
# and then the table with that cluster.
TEST_CASE = self

def result_method(client):
cluster = client.cluster(ZONE, CLUSTER_ID)
table = TEST_CASE._makeOne(TABLE_ID, cluster)
return table.create_column_family(column_family_id,
gc_rule=gc_rule)

self._grpc_client_test_helper('CreateColumnFamily', result_method,
request_pb, response_pb, expected_result,
PROJECT_ID)

def test_create_column_family(self):
self._create_column_family_test_helper(gc_rule=None)

def test_create_column_family_with_gc_rule(self):
from gcloud_bigtable.table import GarbageCollectionRule
gc_rule = GarbageCollectionRule(max_num_versions=1337)
self._create_column_family_test_helper(gc_rule=gc_rule)


class _Cluster(object):

Expand Down
16 changes: 0 additions & 16 deletions gcloud_bigtable/test_table_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,22 +62,6 @@ def test_constructor(self):
('create_scoped', ((klass.SCOPE,),), {}),
])

def test_create_column_family(self):
from gcloud_bigtable._generated import (
bigtable_table_service_messages_pb2 as messages_pb2)

table_name = '%s/tables/%s' % (CLUSTER_NAME, TABLE_ID)
request_obj = messages_pb2.CreateColumnFamilyRequest(
column_family_id=COLUMN_FAMILY_ID,
name=table_name,
)

def call_method(connection):
return connection.create_column_family(
CLUSTER_NAME, TABLE_ID, COLUMN_FAMILY_ID)

self._grpc_call_helper(call_method, 'CreateColumnFamily', request_obj)

def test_update_column_family(self):
from gcloud_bigtable._testing import _MockWithAttachedMethods

Expand Down

0 comments on commit f3460ce

Please sign in to comment.