Skip to content

Commit 3ef5ed1

Browse files
committed
Add API method support for CopyJob.
Factor guts out from LoadFromStorageJob.
1 parent ba46b2c commit 3ef5ed1

2 files changed

Lines changed: 342 additions & 107 deletions

File tree

gcloud/bigquery/job.py

Lines changed: 145 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,112 @@ def state(self):
227227
if status is not None:
228228
return status.get('state')
229229

230+
def _require_client(self, client):
231+
"""Check client or verify over-ride.
232+
233+
:type client: :class:`gcloud.bigquery.client.Client` or ``NoneType``
234+
:param client: the client to use. If not passed, falls back to the
235+
``client`` stored on the current dataset.
236+
237+
:rtype: :class:`gcloud.bigquery.client.Client`
238+
:returns: The client passed in or the currently bound client.
239+
"""
240+
if client is None:
241+
client = self._client
242+
return client
243+
244+
def _scrub_local_properties(self, cleaned):
245+
"""Helper: handle subclass properties in cleaned."""
246+
pass
247+
248+
def _set_properties(self, api_response):
249+
"""Update properties from resource in body of ``api_response``
250+
251+
:type api_response: httplib2.Response
252+
:param api_response: response returned from an API call
253+
"""
254+
cleaned = api_response.copy()
255+
self._scrub_local_properties(cleaned)
256+
257+
statistics = cleaned.get('statistics', {})
258+
if 'creationTime' in statistics:
259+
statistics['creationTime'] = float(statistics['creationTime'])
260+
if 'startTime' in statistics:
261+
statistics['startTime'] = float(statistics['startTime'])
262+
if 'endTime' in statistics:
263+
statistics['endTime'] = float(statistics['endTime'])
264+
265+
self._properties.clear()
266+
self._properties.update(cleaned)
267+
268+
def begin(self, client=None):
269+
"""API call: begin the job via a POST request
270+
271+
See:
272+
https://cloud.google.com/bigquery/docs/reference/v2/jobs/insert
273+
274+
:type client: :class:`gcloud.bigquery.client.Client` or ``NoneType``
275+
:param client: the client to use. If not passed, falls back to the
276+
``client`` stored on the current dataset.
277+
"""
278+
client = self._require_client(client)
279+
path = '/projects/%s/jobs' % (self.project,)
280+
api_response = client.connection.api_request(
281+
method='POST', path=path, data=self._build_resource())
282+
self._set_properties(api_response)
283+
284+
def exists(self, client=None):
285+
"""API call: test for the existence of the job via a GET request
286+
287+
See
288+
https://cloud.google.com/bigquery/docs/reference/v2/jobs/get
289+
290+
:type client: :class:`gcloud.bigquery.client.Client` or ``NoneType``
291+
:param client: the client to use. If not passed, falls back to the
292+
``client`` stored on the current dataset.
293+
"""
294+
client = self._require_client(client)
295+
296+
try:
297+
client.connection.api_request(method='GET', path=self.path,
298+
query_params={'fields': 'id'})
299+
except NotFound:
300+
return False
301+
else:
302+
return True
303+
304+
def reload(self, client=None):
305+
"""API call: refresh job properties via a GET request
306+
307+
See
308+
https://cloud.google.com/bigquery/docs/reference/v2/jobs/get
309+
310+
:type client: :class:`gcloud.bigquery.client.Client` or ``NoneType``
311+
:param client: the client to use. If not passed, falls back to the
312+
``client`` stored on the current dataset.
313+
"""
314+
client = self._require_client(client)
315+
316+
api_response = client.connection.api_request(
317+
method='GET', path=self.path)
318+
self._set_properties(api_response)
319+
320+
def cancel(self, client=None):
321+
"""API call: cancel job via a POST request
322+
323+
See
324+
https://cloud.google.com/bigquery/docs/reference/v2/jobs/cancel
325+
326+
:type client: :class:`gcloud.bigquery.client.Client` or ``NoneType``
327+
:param client: the client to use. If not passed, falls back to the
328+
``client`` stored on the current dataset.
329+
"""
330+
client = self._require_client(client)
331+
332+
api_response = client.connection.api_request(
333+
method='POST', path='%s/cancel' % self.path)
334+
self._set_properties(api_response)
335+
230336

231337
class _LoadConfiguration(object):
232338
"""User-settable configuration options for load jobs."""
@@ -622,20 +728,6 @@ def write_disposition(self):
622728
"""Delete write_disposition."""
623729
del self._configuration._write_disposition
624730

625-
def _require_client(self, client):
626-
"""Check client or verify over-ride.
627-
628-
:type client: :class:`gcloud.bigquery.client.Client` or ``NoneType``
629-
:param client: the client to use. If not passed, falls back to the
630-
``client`` stored on the current dataset.
631-
632-
:rtype: :class:`gcloud.bigquery.client.Client`
633-
:returns: The client passed in or the currently bound client.
634-
"""
635-
if client is None:
636-
client = self._client
637-
return client
638-
639731
def _populate_config_resource(self, configuration):
640732
"""Helper for _build_resource: copy config properties to resource"""
641733
if self.allow_jagged_rows is not None:
@@ -688,95 +780,11 @@ def _build_resource(self):
688780

689781
return resource
690782

691-
def _set_properties(self, api_response):
692-
"""Update properties from resource in body of ``api_response``
693-
694-
:type api_response: httplib2.Response
695-
:param api_response: response returned from an API call
696-
"""
697-
self._properties.clear()
698-
cleaned = api_response.copy()
783+
def _scrub_local_properties(self, cleaned):
784+
"""Helper: handle subclass properties in cleaned."""
699785
schema = cleaned.pop('schema', {'fields': ()})
700786
self.schema = _parse_schema_resource(schema)
701787

702-
statistics = cleaned.get('statistics', {})
703-
if 'creationTime' in statistics:
704-
statistics['creationTime'] = float(statistics['creationTime'])
705-
if 'startTime' in statistics:
706-
statistics['startTime'] = float(statistics['startTime'])
707-
if 'endTime' in statistics:
708-
statistics['endTime'] = float(statistics['endTime'])
709-
710-
self._properties.update(cleaned)
711-
712-
def begin(self, client=None):
713-
"""API call: begin the job via a POST request
714-
715-
See:
716-
https://cloud.google.com/bigquery/docs/reference/v2/jobs/insert
717-
718-
:type client: :class:`gcloud.bigquery.client.Client` or ``NoneType``
719-
:param client: the client to use. If not passed, falls back to the
720-
``client`` stored on the current dataset.
721-
"""
722-
client = self._require_client(client)
723-
path = '/projects/%s/jobs' % (self.project,)
724-
api_response = client.connection.api_request(
725-
method='POST', path=path, data=self._build_resource())
726-
self._set_properties(api_response)
727-
728-
def exists(self, client=None):
729-
"""API call: test for the existence of the job via a GET request
730-
731-
See
732-
https://cloud.google.com/bigquery/docs/reference/v2/jobs/get
733-
734-
:type client: :class:`gcloud.bigquery.client.Client` or ``NoneType``
735-
:param client: the client to use. If not passed, falls back to the
736-
``client`` stored on the current dataset.
737-
"""
738-
client = self._require_client(client)
739-
740-
try:
741-
client.connection.api_request(method='GET', path=self.path,
742-
query_params={'fields': 'id'})
743-
except NotFound:
744-
return False
745-
else:
746-
return True
747-
748-
def reload(self, client=None):
749-
"""API call: refresh job properties via a GET request
750-
751-
See
752-
https://cloud.google.com/bigquery/docs/reference/v2/jobs/get
753-
754-
:type client: :class:`gcloud.bigquery.client.Client` or ``NoneType``
755-
:param client: the client to use. If not passed, falls back to the
756-
``client`` stored on the current dataset.
757-
"""
758-
client = self._require_client(client)
759-
760-
api_response = client.connection.api_request(
761-
method='GET', path=self.path)
762-
self._set_properties(api_response)
763-
764-
def cancel(self, client=None):
765-
"""API call: cancel job via a POST request
766-
767-
See
768-
https://cloud.google.com/bigquery/docs/reference/v2/jobs/cancel
769-
770-
:type client: :class:`gcloud.bigquery.client.Client` or ``NoneType``
771-
:param client: the client to use. If not passed, falls back to the
772-
``client`` stored on the current dataset.
773-
"""
774-
client = self._require_client(client)
775-
776-
api_response = client.connection.api_request(
777-
method='POST', path='%s/cancel' % self.path)
778-
self._set_properties(api_response)
779-
780788

781789
class _CopyConfiguration(object):
782790
"""User-settable configuration options for copy jobs."""
@@ -855,3 +863,40 @@ def write_disposition(self, value):
855863
def write_disposition(self):
856864
"""Delete write_disposition."""
857865
del self._configuration._write_disposition
866+
867+
def _populate_config_resource(self, configuration):
868+
869+
if self.create_disposition is not None:
870+
configuration['createDisposition'] = self.create_disposition
871+
if self.write_disposition is not None:
872+
configuration['writeDisposition'] = self.write_disposition
873+
874+
def _build_resource(self):
875+
"""Generate a resource for ``begin``."""
876+
877+
source_refs = [{
878+
'projectId': table.project,
879+
'datasetId': table.dataset_name,
880+
'tableId': table.name,
881+
} for table in self.sources]
882+
883+
resource = {
884+
'jobReference': {
885+
'projectId': self.project,
886+
'jobId': self.name,
887+
},
888+
'configuration': {
889+
'copy': {
890+
'sourceTables': source_refs,
891+
'destinationTable': {
892+
'projectId': self.destination.project,
893+
'datasetId': self.destination.dataset_name,
894+
'tableId': self.destination.name,
895+
},
896+
},
897+
},
898+
}
899+
configuration = resource['configuration']['copy']
900+
self._populate_config_resource(configuration)
901+
902+
return resource

0 commit comments

Comments
 (0)