diff --git a/.changelog/4455.txt b/.changelog/4455.txt new file mode 100644 index 00000000000..8e2d27f4480 --- /dev/null +++ b/.changelog/4455.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +bigquery: added `status` field to `google_bigquery_job` +``` diff --git a/google/resource_big_query_job.go b/google/resource_big_query_job.go index cc5d676af7b..9b977839d5c 100644 --- a/google/resource_big_query_job.go +++ b/google/resource_big_query_job.go @@ -842,6 +842,70 @@ Creation, truncation and append actions occur as one atomic update upon job comp Default: "US", }, + "status": { + Type: schema.TypeList, + Computed: true, + Description: `The status of this job. Examine this value when polling an asynchronous job to see if the job is complete.`, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "error_result": { + Type: schema.TypeList, + Computed: true, + Description: `Final error result of the job. If present, indicates that the job has completed and was unsuccessful.`, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "location": { + Type: schema.TypeString, + Optional: true, + Description: `Specifies where the error occurred, if present.`, + }, + "message": { + Type: schema.TypeString, + Optional: true, + Description: `A human-readable description of the error.`, + }, + "reason": { + Type: schema.TypeString, + Optional: true, + Description: `A short error code that summarizes the error.`, + }, + }, + }, + }, + "errors": { + Type: schema.TypeList, + Computed: true, + Description: `The first errors encountered during the running of the job. The final message +includes the number of errors that caused the process to stop. Errors here do +not necessarily mean that the job has not completed or was unsuccessful.`, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "location": { + Type: schema.TypeString, + Optional: true, + Description: `Specifies where the error occurred, if present.`, + }, + "message": { + Type: schema.TypeString, + Optional: true, + Description: `A human-readable description of the error.`, + }, + "reason": { + Type: schema.TypeString, + Optional: true, + Description: `A short error code that summarizes the error.`, + }, + }, + }, + }, + "state": { + Type: schema.TypeString, + Computed: true, + Description: `Running state of the job. Valid states include 'PENDING', 'RUNNING', and 'DONE'.`, + }, + }, + }, + }, "user_email": { Type: schema.TypeString, Computed: true, @@ -1027,6 +1091,9 @@ func resourceBigQueryJobRead(d *schema.ResourceData, meta interface{}) error { } } } + if err := d.Set("status", flattenBigQueryJobStatus(res["status"], d, config)); err != nil { + return fmt.Errorf("Error reading Job: %s", err) + } return nil } @@ -1744,6 +1811,88 @@ func flattenBigQueryJobJobReferenceLocation(v interface{}, d *schema.ResourceDat return v } +func flattenBigQueryJobStatus(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["error_result"] = + flattenBigQueryJobStatusErrorResult(original["errorResult"], d, config) + transformed["errors"] = + flattenBigQueryJobStatusErrors(original["errors"], d, config) + transformed["state"] = + flattenBigQueryJobStatusState(original["state"], d, config) + return []interface{}{transformed} +} +func flattenBigQueryJobStatusErrorResult(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["reason"] = + flattenBigQueryJobStatusErrorResultReason(original["reason"], d, config) + transformed["location"] = + flattenBigQueryJobStatusErrorResultLocation(original["location"], d, config) + transformed["message"] = + flattenBigQueryJobStatusErrorResultMessage(original["message"], d, config) + return []interface{}{transformed} +} +func flattenBigQueryJobStatusErrorResultReason(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenBigQueryJobStatusErrorResultLocation(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenBigQueryJobStatusErrorResultMessage(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenBigQueryJobStatusErrors(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return v + } + l := v.([]interface{}) + transformed := make([]interface{}, 0, len(l)) + for _, raw := range l { + original := raw.(map[string]interface{}) + if len(original) < 1 { + // Do not include empty json objects coming back from the api + continue + } + transformed = append(transformed, map[string]interface{}{ + "reason": flattenBigQueryJobStatusErrorsReason(original["reason"], d, config), + "location": flattenBigQueryJobStatusErrorsLocation(original["location"], d, config), + "message": flattenBigQueryJobStatusErrorsMessage(original["message"], d, config), + }) + } + return transformed +} +func flattenBigQueryJobStatusErrorsReason(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenBigQueryJobStatusErrorsLocation(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenBigQueryJobStatusErrorsMessage(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenBigQueryJobStatusState(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + func expandBigQueryJobConfiguration(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { transformed := make(map[string]interface{}) transformedJobType, err := expandBigQueryJobConfigurationJobType(d.Get("job_type"), d, config) diff --git a/google/resource_big_query_job_generated_test.go b/google/resource_big_query_job_generated_test.go index f9b8ea4208c..f8020c67480 100644 --- a/google/resource_big_query_job_generated_test.go +++ b/google/resource_big_query_job_generated_test.go @@ -41,7 +41,7 @@ func TestAccBigQueryJob_bigqueryJobQueryExample(t *testing.T) { ResourceName: "google_bigquery_job.job", ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"etag"}, + ImportStateVerifyIgnore: []string{"etag", "status.0.state"}, }, }, }) @@ -109,7 +109,7 @@ func TestAccBigQueryJob_bigqueryJobQueryTableReferenceExample(t *testing.T) { ResourceName: "google_bigquery_job.job", ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"etag", "query.0.default_dataset.0.dataset_id", "query.0.destination_table.0.table_id"}, + ImportStateVerifyIgnore: []string{"etag", "query.0.default_dataset.0.dataset_id", "query.0.destination_table.0.table_id", "status.0.state"}, }, }, }) @@ -179,7 +179,7 @@ func TestAccBigQueryJob_bigqueryJobLoadExample(t *testing.T) { ResourceName: "google_bigquery_job.job", ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"etag"}, + ImportStateVerifyIgnore: []string{"etag", "status.0.state"}, }, }, }) @@ -248,7 +248,7 @@ func TestAccBigQueryJob_bigqueryJobLoadTableReferenceExample(t *testing.T) { ResourceName: "google_bigquery_job.job", ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"etag", "load.0.destination_table.0.table_id"}, + ImportStateVerifyIgnore: []string{"etag", "load.0.destination_table.0.table_id", "status.0.state"}, }, }, }) @@ -316,7 +316,7 @@ func TestAccBigQueryJob_bigqueryJobCopyExample(t *testing.T) { ResourceName: "google_bigquery_job.job", ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"etag"}, + ImportStateVerifyIgnore: []string{"etag", "status.0.state"}, }, }, }) @@ -471,7 +471,7 @@ func TestAccBigQueryJob_bigqueryJobCopyTableReferenceExample(t *testing.T) { ResourceName: "google_bigquery_job.job", ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"etag", "copy.0.destination_table.0.table_id", "copy.0.source_tables.0.table_id", "copy.0.source_tables.1.table_id"}, + ImportStateVerifyIgnore: []string{"etag", "copy.0.destination_table.0.table_id", "copy.0.source_tables.0.table_id", "copy.0.source_tables.1.table_id", "status.0.state"}, }, }, }) @@ -619,7 +619,7 @@ func TestAccBigQueryJob_bigqueryJobExtractExample(t *testing.T) { ResourceName: "google_bigquery_job.job", ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"etag"}, + ImportStateVerifyIgnore: []string{"etag", "status.0.state"}, }, }, }) @@ -705,7 +705,7 @@ func TestAccBigQueryJob_bigqueryJobExtractTableReferenceExample(t *testing.T) { ResourceName: "google_bigquery_job.job", ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"etag", "extract.0.source_table.0.table_id"}, + ImportStateVerifyIgnore: []string{"etag", "extract.0.source_table.0.table_id", "status.0.state"}, }, }, }) diff --git a/google/resource_bigquery_job_test.go b/google/resource_bigquery_job_test.go index 1ad5ac45607..8bd98bd1a15 100644 --- a/google/resource_bigquery_job_test.go +++ b/google/resource_bigquery_job_test.go @@ -33,7 +33,7 @@ func TestAccBigQueryJob_withLocation(t *testing.T) { ImportStateId: importID, ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"etag"}, + ImportStateVerifyIgnore: []string{"etag", "status.0.state"}, }, }, }) diff --git a/website/docs/r/bigquery_job.html.markdown b/website/docs/r/bigquery_job.html.markdown index 30d26aca265..2247abe7219 100644 --- a/website/docs/r/bigquery_job.html.markdown +++ b/website/docs/r/bigquery_job.html.markdown @@ -920,6 +920,54 @@ In addition to the arguments listed above, the following computed attributes are * `job_type` - The type of the job. +* `status` - + The status of this job. Examine this value when polling an asynchronous job to see if the job is complete. + Structure is documented below. + + +The `status` block contains: + +* `error_result` - + Final error result of the job. If present, indicates that the job has completed and was unsuccessful. + Structure is documented below. + +* `errors` - + The first errors encountered during the running of the job. The final message + includes the number of errors that caused the process to stop. Errors here do + not necessarily mean that the job has not completed or was unsuccessful. + Structure is documented below. + +* `state` - + Running state of the job. Valid states include 'PENDING', 'RUNNING', and 'DONE'. + + +The `error_result` block contains: + +* `reason` - + (Optional) + A short error code that summarizes the error. + +* `location` - + (Optional) + Specifies where the error occurred, if present. + +* `message` - + (Optional) + A human-readable description of the error. + +The `errors` block contains: + +* `reason` - + (Optional) + A short error code that summarizes the error. + +* `location` - + (Optional) + Specifies where the error occurred, if present. + +* `message` - + (Optional) + A human-readable description of the error. ## Timeouts