Repository: googleapis/google-cloud-python

Issue:
Job.type is referenced throughout bigquery-usage but is not implemented
From #1397

/cc @ollikaven

Job.job_id is populated before job creation in bigquery-usage
The [`job_id`](https://github.com/GoogleCloudPlatform/gcloud-python/blob/ffe5869aa7c31bd82483e4d52adacbf83cf0fd69/gcloud/bigquery/job.py#L150-L156) property uses the `_properties` returned from the server. It seems the original intent was a `uuid.uuid4()` on instance creation.



Discussion:

Resolving patch:
diff --git a/gcloud/bigquery/job.py b/gcloud/bigquery/job.py
--- a/gcloud/bigquery/job.py
+++ b/gcloud/bigquery/job.py
@@ -128,6 +128,15 @@ def __init__(self, name, client):
         super(_AsyncJob, self).__init__(client)
         self.name = name
 
+    @property
+    def job_type(self):
+        """Type of job
+
+        :rtype: string
+        :returns: one of 'load', 'copy', 'extract', 'query'
+        """
+        return self._JOB_TYPE
+
     @property
     def path(self):
         """URL path for the job's APIs.
@@ -146,15 +155,6 @@ def etag(self):
         """
         return self._properties.get('etag')
 
-    @property
-    def job_id(self):
-        """ID for the job resource.
-
-        :rtype: string, or ``NoneType``
-        :returns: the ID (None until set from the server).
-        """
-        return self._properties.get('id')
-
     @property
     def self_link(self):
         """URL for the job resource.
@@ -288,10 +288,10 @@ def _get_resource_config(cls, resource):
                            '["jobReference"]["jobId"]')
         name = resource['jobReference']['jobId']
         if ('configuration' not in resource or
-                cls._CONFIG_KEY not in resource['configuration']):
+                cls._JOB_TYPE not in resource['configuration']):
             raise KeyError('Resource lacks required configuration: '
-                           '["configuration"]["%s"]' % cls._CONFIG_KEY)
-        config = resource['configuration'][cls._CONFIG_KEY]
+                           '["configuration"]["%s"]' % cls._JOB_TYPE)
+        config = resource['configuration'][cls._JOB_TYPE]
         return name, config
 
     def begin(self, client=None):
@@ -403,7 +403,7 @@ class LoadTableFromStorageJob(_AsyncJob):
     """
 
     _schema = None
-    _CONFIG_KEY = 'load'
+    _JOB_TYPE = 'load'
 
     def __init__(self, name, destination, source_uris, client, schema=()):
         super(LoadTableFromStorageJob, self).__init__(name, client)
@@ -568,7 +568,7 @@ def _build_resource(self):
                 'jobId': self.name,
             },
             'configuration': {
-                self._CONFIG_KEY: {
+                self._JOB_TYPE: {
                     'sourceUris': self.source_uris,
                     'destinationTable': {
                         'projectId': self.destination.project,
@@ -578,7 +578,7 @@ def _build_resource(self):
                 },
             },
         }
-        configuration = resource['configuration'][self._CONFIG_KEY]
+        configuration = resource['configuration'][self._JOB_TYPE]
         self._populate_config_resource(configuration)
 
         if len(self.schema) > 0:
@@ -647,7 +647,7 @@ class CopyJob(_AsyncJob):
                    for the dataset (which requires a project).
     """
 
-    _CONFIG_KEY = 'copy'
+    _JOB_TYPE = 'copy'
 
     def __init__(self, name, destination, sources, client):
         super(CopyJob, self).__init__(name, client)
@@ -687,7 +687,7 @@ def _build_resource(self):
                 'jobId': self.name,
             },
             'configuration': {
-                self._CONFIG_KEY: {
+                self._JOB_TYPE: {
                     'sourceTables': source_refs,
                     'destinationTable': {
                         'projectId': self.destination.project,
@@ -697,7 +697,7 @@ def _build_resource(self):
                 },
             },
         }
-        configuration = resource['configuration'][self._CONFIG_KEY]
+        configuration = resource['configuration'][self._JOB_TYPE]
         self._populate_config_resource(configuration)
 
         return resource
@@ -763,7 +763,7 @@ class ExtractTableToStorageJob(_AsyncJob):
     :param client: A client which holds credentials and project configuration
                    for the dataset (which requires a project).
     """
-    _CONFIG_KEY = 'extract'
+    _JOB_TYPE = 'extract'
 
     def __init__(self, name, source, destination_uris, client):
         super(ExtractTableToStorageJob, self).__init__(name, client)
@@ -817,13 +817,13 @@ def _build_resource(self):
                 'jobId': self.name,
             },
             'configuration': {
-                self._CONFIG_KEY: {
+                self._JOB_TYPE: {
                     'sourceTable': source_ref,
                     'destinationUris': self.destination_uris,
                 },
             },
         }
-        configuration = resource['configuration'][self._CONFIG_KEY]
+        configuration = resource['configuration'][self._JOB_TYPE]
         self._populate_config_resource(configuration)
 
         return resource
@@ -885,7 +885,7 @@ class QueryJob(_AsyncJob):
     :param client: A client which holds credentials and project configuration
                    for the dataset (which requires a project).
     """
-    _CONFIG_KEY = 'query'
+    _JOB_TYPE = 'query'
 
     def __init__(self, name, query, client):
         super(QueryJob, self).__init__(name, client)
@@ -972,12 +972,12 @@ def _build_resource(self):
                 'jobId': self.name,
             },
             'configuration': {
-                self._CONFIG_KEY: {
+                self._JOB_TYPE: {
                     'query': self.query,
                 },
             },
         }
-        configuration = resource['configuration'][self._CONFIG_KEY]
+        configuration = resource['configuration'][self._JOB_TYPE]
         self._populate_config_resource(configuration)
 
         return resource


Test patch:
diff --git a/gcloud/bigquery/test_client.py b/gcloud/bigquery/test_client.py
--- a/gcloud/bigquery/test_client.py
+++ b/gcloud/bigquery/test_client.py
@@ -233,7 +233,7 @@ def test_list_jobs_defaults(self):
         for found, expected in zip(jobs, DATA['jobs']):
             name = expected['jobReference']['jobId']
             self.assertTrue(isinstance(found, JOB_TYPES[name]))
-            self.assertEqual(found.job_id, expected['id'])
+            self.assertEqual(found.name, name)
         self.assertEqual(token, TOKEN)
 
         self.assertEqual(len(conn._requested), 1)
@@ -285,7 +285,7 @@ def test_list_jobs_load_job_wo_sourceUris(self):
         for found, expected in zip(jobs, DATA['jobs']):
             name = expected['jobReference']['jobId']
             self.assertTrue(isinstance(found, JOB_TYPES[name]))
-            self.assertEqual(found.job_id, expected['id'])
+            self.assertEqual(found.name, name)
         self.assertEqual(token, TOKEN)
 
         self.assertEqual(len(conn._requested), 1)
diff --git a/gcloud/bigquery/test_job.py b/gcloud/bigquery/test_job.py
--- a/gcloud/bigquery/test_job.py
+++ b/gcloud/bigquery/test_job.py
@@ -71,7 +71,6 @@ def _makeResource(self, started=False, ended=False):
     def _verifyInitialReadonlyProperties(self, job):
         # root elements of resource
         self.assertEqual(job.etag, None)
-        self.assertEqual(job.job_id, None)
         self.assertEqual(job.self_link, None)
         self.assertEqual(job.user_email, None)
 
@@ -88,8 +87,6 @@ def _verifyInitialReadonlyProperties(self, job):
     def _verifyReadonlyResourceProperties(self, job, resource):
         from datetime import timedelta
 
-        self.assertEqual(job.job_id, self.JOB_ID)
-
         statistics = resource.get('statistics', {})
 
         if 'creationTime' in statistics:
@@ -238,6 +235,7 @@ def test_ctor(self):
         self.assertTrue(job.destination is table)
         self.assertEqual(list(job.source_uris), [self.SOURCE1])
         self.assertTrue(job._client is client)
+        self.assertEqual(job.job_type, self.JOB_TYPE)
         self.assertEqual(
             job.path,
             '/projects/%s/jobs/%s' % (self.PROJECT, self.JOB_NAME))
@@ -336,7 +334,6 @@ def test_props_set_by_server(self):
         load_stats['outputRows'] = 345
 
         self.assertEqual(job.etag, 'ETAG')
-        self.assertEqual(job.job_id, JOB_ID)
         self.assertEqual(job.self_link, URL)
         self.assertEqual(job.user_email, EMAIL)
 
@@ -692,6 +689,7 @@ def test_ctor(self):
         self.assertTrue(job.destination is destination)
         self.assertEqual(job.sources, [source])
         self.assertTrue(job._client is client)
+        self.assertEqual(job.job_type, self.JOB_TYPE)
         self.assertEqual(
             job.path,
             '/projects/%s/jobs/%s' % (self.PROJECT, self.JOB_NAME))
@@ -990,6 +988,7 @@ def test_ctor(self):
         self.assertEqual(job.source, source)
         self.assertEqual(job.destination_uris, [self.DESTINATION_URI])
         self.assertTrue(job._client is client)
+        self.assertEqual(job.job_type, self.JOB_TYPE)
         self.assertEqual(
             job.path,
             '/projects/%s/jobs/%s' % (self.PROJECT, self.JOB_NAME))
@@ -1297,6 +1296,7 @@ def test_ctor(self):
         job = self._makeOne(self.JOB_NAME, self.QUERY, client)
         self.assertEqual(job.query, self.QUERY)
         self.assertTrue(job._client is client)
+        self.assertEqual(job.job_type, self.JOB_TYPE)
         self.assertEqual(
             job.path,
             '/projects/%s/jobs/%s' % (self.PROJECT, self.JOB_NAME))