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

Bigquery table api methods #1023

Merged
merged 7 commits into from
Aug 5, 2015
Merged
Show file tree
Hide file tree
Changes from 2 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
15 changes: 15 additions & 0 deletions gcloud/bigquery/dataset.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

from gcloud.exceptions import NotFound
from gcloud.bigquery._helpers import _datetime_from_prop
from gcloud.bigquery.table import Table


class Dataset(object):
Expand Down Expand Up @@ -353,3 +354,17 @@ def delete(self, client=None):
"""
client = self._require_client(client)
client.connection.api_request(method='DELETE', path=self.path)

def table(self, name, schema=()):
"""Construct a table bound to this dataset.

:type name: string
:param name: Name of the table.

:rtype: :class:`gcloud.bigquery.table.Table`
:returns: a new ``Table`` instance

:type schema: list of :class:`gcloud.bigquery.table.SchemaField`
:param schema: The table's schema

This comment was marked as spam.

This comment was marked as spam.

"""
return Table(name, dataset=self, schema=schema)
195 changes: 195 additions & 0 deletions gcloud/bigquery/table.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import six

from gcloud.exceptions import NotFound
from gcloud.bigquery._helpers import _datetime_from_prop
from gcloud.bigquery._helpers import _prop_from_datetime

Expand Down Expand Up @@ -293,3 +294,197 @@ def view_query(self, value):
def view_query(self):
"""Delete SQL query defining the table as a view."""
self._properties.pop('view', None)

def _require_client(self, client):
"""Check client or verify over-ride.

:type client: :class:`gcloud.bigquery.client.Client` or ``NoneType``
:param client: the client to use. If not passed, falls back to the
``client`` stored on the current dataset.

:rtype: :class:`gcloud.bigquery.client.Client`
:returns: The client passed in or the currently bound client.
"""
if client is None:
client = self._dataset._client
return client

def _set_properties(self, api_response):
"""Update properties from resource in body of ``api_response``

:type api_response: httplib2.Response
:param api_response: response returned from an API call
"""
self._properties.clear()
cleaned = api_response.copy()
cleaned['creationTime'] = float(cleaned['creationTime'])
cleaned['lastModifiedTime'] = float(cleaned['lastModifiedTime'])
if 'expirationTime' in cleaned:
cleaned['expirationTime'] = float(cleaned['expirationTime'])

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

self._properties.update(cleaned)

def _build_schema_resource(self, fields=None):
"""Generate a resource fragment for table's schema."""
if fields is None:
fields = self._schema

This comment was marked as spam.

This comment was marked as spam.

infos = []
for field in fields:
info = {'name': field.name,
'type': field.field_type,
'mode': field.mode}
if field.description is not None:
info['description'] = field.description
if field.fields is not None:
info['fields'] = self._build_schema_resource(field.fields)
infos.append(info)
return infos

def _build_resource(self):
"""Generate a resource for ``create`` or ``update``."""
resource = {
'tableReference': {
'projectId': self._dataset.project,
'datasetId': self._dataset.name,
'tableId': self.name},
'schema': {'fields': self._build_schema_resource()},
}
if self.description is not None:
resource['description'] = self.description

if self.expires is not None:
value = _prop_from_datetime(self.expires)
resource['expirationTime'] = value

if self.friendly_name is not None:
resource['friendlyName'] = self.friendly_name

if self.location is not None:
resource['location'] = self.location

if self.view_query is not None:
view = resource['view'] = {}
view['query'] = self.view_query

return resource

def create(self, client=None):
"""API call: create the dataset via a PUT request

See:
https://cloud.google.com/bigquery/reference/rest/v2/tables/insert

:type client: :class:`gcloud.bigquery.client.Client` or ``NoneType``
:param client: the client to use. If not passed, falls back to the
``client`` stored on the current dataset.
"""
client = self._require_client(client)
path = '/projects/%s/datasets/%s/tables' % (
self._dataset.project, self._dataset.name)
api_response = client.connection.api_request(
method='POST', path=path, data=self._build_resource())
self._set_properties(api_response)

def exists(self, client=None):
"""API call: test for the existence of the table via a GET request

See
https://cloud.google.com/bigquery/docs/reference/v2/tables/get

:type client: :class:`gcloud.bigquery.client.Client` or ``NoneType``
:param client: the client to use. If not passed, falls back to the
``client`` stored on the current dataset.
"""
client = self._require_client(client)

try:
client.connection.api_request(method='GET', path=self.path,
query_params={'fields': 'id'})
except NotFound:
return False
else:
return True

def reload(self, client=None):
"""API call: refresh table properties via a GET request

See
https://cloud.google.com/bigquery/docs/reference/v2/tables/get

:type client: :class:`gcloud.bigquery.client.Client` or ``NoneType``
:param client: the client to use. If not passed, falls back to the
``client`` stored on the current dataset.
"""
client = self._require_client(client)

api_response = client.connection.api_request(
method='GET', path=self.path)
self._set_properties(api_response)

def patch(self, client=None, **kw):
"""API call: update individual table properties via a PATCH request

See
https://cloud.google.com/bigquery/docs/reference/v2/tables/patch

This comment was marked as spam.


:type client: :class:`gcloud.bigquery.client.Client` or ``NoneType``
:param client: the client to use. If not passed, falls back to the
``client`` stored on the current dataset.

:type kw: ``dict``
:param kw: properties to be patched.

:raises: ValueError for invalid value types.
"""
client = self._require_client(client)

partial = {}

if 'expires' in kw:
value = kw['expires']
if not isinstance(value, datetime.datetime) and value is not None:
raise ValueError("Pass a datetime, or None")
partial['expirationTime'] = _prop_from_datetime(value)

if 'description' in kw:
partial['description'] = kw['description']

if 'friendly_name' in kw:
partial['friendlyName'] = kw['friendly_name']

if 'location' in kw:
partial['location'] = kw['location']

if 'view_query' in kw:
partial['view'] = {'query': kw['view_query']}

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

api_response = client.connection.api_request(
method='PATCH', path=self.path, data=partial)
self._set_properties(api_response)

def update(self, client=None):
"""API call: update table properties via a PUT request

See
https://cloud.google.com/bigquery/docs/reference/v2/tables/update

:type client: :class:`gcloud.bigquery.client.Client` or ``NoneType``
:param client: the client to use. If not passed, falls back to the
``client`` stored on the current dataset.
"""
client = self._require_client(client)
api_response = client.connection.api_request(
method='PUT', path=self.path, data=self._build_resource())
self._set_properties(api_response)

def delete(self, client=None):
"""API call: delete the table via a DELETE request

See:
https://cloud.google.com/bigquery/reference/rest/v2/tables/delete

:type client: :class:`gcloud.bigquery.client.Client` or ``NoneType``
:param client: the client to use. If not passed, falls back to the
``client`` stored on the current dataset.
"""
client = self._require_client(client)
client.connection.api_request(method='DELETE', path=self.path)
25 changes: 25 additions & 0 deletions gcloud/bigquery/test_dataset.py
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,31 @@ def test_delete_w_alternate_client(self):
self.assertEqual(req['method'], 'DELETE')
self.assertEqual(req['path'], '/%s' % PATH)

def test_table_wo_schema(self):
from gcloud.bigquery.table import Table
conn = _Connection({})
CLIENT = _Client(project=self.PROJECT, connection=conn)
dataset = self._makeOne(self.DS_NAME, client=CLIENT)
table = dataset.table('table_name')
self.assertTrue(isinstance(table, Table))
self.assertEqual(table.name, 'table_name')
self.assertTrue(table._dataset is dataset)
self.assertEqual(table.schema, [])

def test_table_w_schema(self):
from gcloud.bigquery.table import SchemaField
from gcloud.bigquery.table import Table
conn = _Connection({})
CLIENT = _Client(project=self.PROJECT, connection=conn)
dataset = self._makeOne(self.DS_NAME, client=CLIENT)
full_name = SchemaField('full_name', 'STRING', mode='REQUIRED')
age = SchemaField('age', 'INTEGER', mode='REQUIRED')
table = dataset.table('table_name', schema=[full_name, age])
self.assertTrue(isinstance(table, Table))
self.assertEqual(table.name, 'table_name')
self.assertTrue(table._dataset is dataset)
self.assertEqual(table.schema, [full_name, age])


class _Client(object):

Expand Down
Loading