gcloud 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +8 -8
- data/AUTHENTICATION.md +3 -3
- data/CHANGELOG.md +12 -0
- data/OVERVIEW.md +30 -0
- data/lib/gcloud.rb +126 -9
- data/lib/gcloud/bigquery.rb +399 -0
- data/lib/gcloud/bigquery/connection.rb +592 -0
- data/lib/gcloud/bigquery/copy_job.rb +98 -0
- data/lib/gcloud/bigquery/credentials.rb +29 -0
- data/lib/gcloud/bigquery/data.rb +134 -0
- data/lib/gcloud/bigquery/dataset.rb +662 -0
- data/lib/gcloud/bigquery/dataset/list.rb +51 -0
- data/lib/gcloud/bigquery/errors.rb +62 -0
- data/lib/gcloud/bigquery/extract_job.rb +117 -0
- data/lib/gcloud/bigquery/insert_response.rb +80 -0
- data/lib/gcloud/bigquery/job.rb +283 -0
- data/lib/gcloud/bigquery/job/list.rb +55 -0
- data/lib/gcloud/bigquery/load_job.rb +199 -0
- data/lib/gcloud/bigquery/project.rb +512 -0
- data/lib/gcloud/bigquery/query_data.rb +135 -0
- data/lib/gcloud/bigquery/query_job.rb +151 -0
- data/lib/gcloud/bigquery/table.rb +827 -0
- data/lib/gcloud/bigquery/table/list.rb +55 -0
- data/lib/gcloud/bigquery/view.rb +419 -0
- data/lib/gcloud/credentials.rb +3 -3
- data/lib/gcloud/datastore.rb +15 -3
- data/lib/gcloud/datastore/credentials.rb +3 -2
- data/lib/gcloud/datastore/dataset.rb +5 -1
- data/lib/gcloud/datastore/transaction.rb +1 -1
- data/lib/gcloud/pubsub.rb +14 -3
- data/lib/gcloud/pubsub/credentials.rb +4 -4
- data/lib/gcloud/pubsub/project.rb +5 -1
- data/lib/gcloud/pubsub/topic.rb +5 -0
- data/lib/gcloud/storage.rb +14 -24
- data/lib/gcloud/storage/bucket.rb +10 -4
- data/lib/gcloud/storage/credentials.rb +3 -2
- data/lib/gcloud/storage/file.rb +8 -1
- data/lib/gcloud/storage/project.rb +5 -1
- data/lib/gcloud/upload.rb +54 -0
- data/lib/gcloud/version.rb +1 -1
- metadata +78 -2
@@ -0,0 +1,51 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright 2015 Google Inc. All rights reserved.
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
|
16
|
+
module Gcloud
|
17
|
+
module Bigquery
|
18
|
+
class Dataset
|
19
|
+
##
|
20
|
+
# Dataset::List is a special case Array with additional values.
|
21
|
+
class List < DelegateClass(::Array)
|
22
|
+
##
|
23
|
+
# If not empty, indicates that there are more records that match
|
24
|
+
# the request and this value should be passed to continue.
|
25
|
+
attr_accessor :token
|
26
|
+
|
27
|
+
# A hash of this page of results.
|
28
|
+
attr_accessor :etag
|
29
|
+
|
30
|
+
##
|
31
|
+
# Create a new Dataset::List with an array of datasets.
|
32
|
+
def initialize arr = []
|
33
|
+
super arr
|
34
|
+
end
|
35
|
+
|
36
|
+
##
|
37
|
+
# New Dataset::List from a response object.
|
38
|
+
def self.from_resp resp, conn #:nodoc:
|
39
|
+
datasets = List.new(Array(resp.data["datasets"]).map do |gapi_object|
|
40
|
+
Dataset.from_gapi gapi_object, conn
|
41
|
+
end)
|
42
|
+
datasets.instance_eval do
|
43
|
+
@token = resp.data["nextPageToken"]
|
44
|
+
@etag = resp.data["etag"]
|
45
|
+
end
|
46
|
+
datasets
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright 2015 Google Inc. All rights reserved.
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
|
16
|
+
module Gcloud
|
17
|
+
module Bigquery
|
18
|
+
##
|
19
|
+
# Base BigQuery exception class.
|
20
|
+
class Error < Gcloud::Error
|
21
|
+
end
|
22
|
+
|
23
|
+
##
|
24
|
+
# Raised when an API call is not successful.
|
25
|
+
class ApiError < Error
|
26
|
+
##
|
27
|
+
# The code of the error.
|
28
|
+
attr_reader :code
|
29
|
+
|
30
|
+
##
|
31
|
+
# The errors encountered.
|
32
|
+
attr_reader :errors
|
33
|
+
|
34
|
+
def initialize message, code, errors = [] #:nodoc:
|
35
|
+
super message
|
36
|
+
@code = code
|
37
|
+
@errors = errors
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.from_response resp #:nodoc:
|
41
|
+
if resp.data? && resp.data["error"]
|
42
|
+
from_response_data resp.data["error"]
|
43
|
+
else
|
44
|
+
from_response_status resp
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.from_response_data error #:nodoc:
|
49
|
+
new error["message"], error["code"], error["errors"]
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.from_response_status resp #:nodoc:
|
53
|
+
if resp.status == 404
|
54
|
+
new "#{resp.error_message}: #{resp.request.uri.request_uri}",
|
55
|
+
resp.status
|
56
|
+
else
|
57
|
+
new resp.error_message, resp.status
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright 2015 Google Inc. All rights reserved.
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
|
16
|
+
module Gcloud
|
17
|
+
module Bigquery
|
18
|
+
##
|
19
|
+
# = ExtractJob
|
20
|
+
#
|
21
|
+
# A Job subclass representing an export operation that may be performed
|
22
|
+
# on a Table. A ExtractJob instance is created when you call Table#extract.
|
23
|
+
#
|
24
|
+
# See {Exporting Data From
|
25
|
+
# BigQuery}[https://cloud.google.com/bigquery/exporting-data-from-bigquery]
|
26
|
+
# and the {Jobs API
|
27
|
+
# reference}[https://cloud.google.com/bigquery/docs/reference/v2/jobs]
|
28
|
+
# for details.
|
29
|
+
#
|
30
|
+
class ExtractJob < Job
|
31
|
+
##
|
32
|
+
# The URI or URIs representing the Google Cloud Storage files to which
|
33
|
+
# the data is exported.
|
34
|
+
def destinations
|
35
|
+
Array config["extract"]["destinationUris"]
|
36
|
+
end
|
37
|
+
|
38
|
+
##
|
39
|
+
# The table from which the data is exported. This is the table upon
|
40
|
+
# which Table#extract was called. Returns a Table instance.
|
41
|
+
def source
|
42
|
+
table = config["extract"]["sourceTable"]
|
43
|
+
return nil unless table
|
44
|
+
retrieve_table table["projectId"],
|
45
|
+
table["datasetId"],
|
46
|
+
table["tableId"]
|
47
|
+
end
|
48
|
+
|
49
|
+
##
|
50
|
+
# Checks if the export operation compresses the data using gzip. The
|
51
|
+
# default is +false+.
|
52
|
+
def compression?
|
53
|
+
val = config["extract"]["compression"]
|
54
|
+
val == "GZIP"
|
55
|
+
end
|
56
|
+
|
57
|
+
##
|
58
|
+
# Checks if the destination format for the data is {newline-delimited
|
59
|
+
# JSON}[http://jsonlines.org/]. The default is +false+.
|
60
|
+
def json?
|
61
|
+
val = config["extract"]["destinationFormat"]
|
62
|
+
val == "NEWLINE_DELIMITED_JSON"
|
63
|
+
end
|
64
|
+
|
65
|
+
##
|
66
|
+
# Checks if the destination format for the data is CSV. Tables with nested
|
67
|
+
# or repeated fields cannot be exported as CSV. The default is +true+.
|
68
|
+
def csv?
|
69
|
+
val = config["extract"]["destinationFormat"]
|
70
|
+
return true if val.nil?
|
71
|
+
val == "CSV"
|
72
|
+
end
|
73
|
+
|
74
|
+
##
|
75
|
+
# Checks if the destination format for the data is
|
76
|
+
# {Avro}[http://avro.apache.org/]. The default is +false+.
|
77
|
+
def avro?
|
78
|
+
val = config["extract"]["destinationFormat"]
|
79
|
+
val == "AVRO"
|
80
|
+
end
|
81
|
+
|
82
|
+
##
|
83
|
+
# The symbol the operation uses to delimit fields in the exported data.
|
84
|
+
# The default is a comma (,).
|
85
|
+
def delimiter
|
86
|
+
val = config["extract"]["fieldDelimiter"]
|
87
|
+
val = "," if val.nil?
|
88
|
+
val
|
89
|
+
end
|
90
|
+
|
91
|
+
##
|
92
|
+
# Checks if the exported data contains a header row. The default is
|
93
|
+
# +true+.
|
94
|
+
def print_header?
|
95
|
+
val = config["extract"]["printHeader"]
|
96
|
+
val = true if val.nil?
|
97
|
+
val
|
98
|
+
end
|
99
|
+
|
100
|
+
##
|
101
|
+
# The count of files per destination URI or URI pattern specified in
|
102
|
+
# #destinations. Returns an Array of values in the same order as the URI
|
103
|
+
# patterns.
|
104
|
+
def destinations_file_counts
|
105
|
+
Array stats["extract"]["destinationUriFileCounts"]
|
106
|
+
end
|
107
|
+
|
108
|
+
##
|
109
|
+
# The count of files per destination URI or URI pattern specified in
|
110
|
+
# #destinations. Returns a Hash with the URI patterns as keys and the
|
111
|
+
# counts as values.
|
112
|
+
def destinations_counts
|
113
|
+
Hash[destinations.zip destinations_file_counts]
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright 2015 Google Inc. All rights reserved.
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
|
16
|
+
module Gcloud
|
17
|
+
module Bigquery
|
18
|
+
##
|
19
|
+
# InsertResponse
|
20
|
+
class InsertResponse
|
21
|
+
def initialize rows, gapi #:nodoc:
|
22
|
+
@rows = rows
|
23
|
+
@gapi = gapi
|
24
|
+
end
|
25
|
+
|
26
|
+
def success?
|
27
|
+
error_count.zero?
|
28
|
+
end
|
29
|
+
|
30
|
+
def insert_count
|
31
|
+
@insert_count ||= @rows.count - error_count
|
32
|
+
end
|
33
|
+
|
34
|
+
def error_count
|
35
|
+
@error_count ||= Array(@gapi["insertErrors"]).count
|
36
|
+
end
|
37
|
+
|
38
|
+
def insert_errors
|
39
|
+
@insert_errors ||= begin
|
40
|
+
Array(@gapi["insertErrors"]).map do |ie|
|
41
|
+
row = @rows[ie["index"]]
|
42
|
+
errors = ie["errors"]
|
43
|
+
InsertError.new row, errors
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def error_rows
|
49
|
+
@error_rows ||= begin
|
50
|
+
Array(@gapi["insertErrors"]).map do |ie|
|
51
|
+
@rows[ie["index"]]
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def errors_for row
|
57
|
+
ie = insert_errors.detect { |e| e.row == row }
|
58
|
+
return ie.errors if ie
|
59
|
+
[]
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.from_gapi rows, gapi #:nodoc:
|
63
|
+
gapi = gapi.to_hash if gapi.respond_to? :to_hash
|
64
|
+
new rows, gapi
|
65
|
+
end
|
66
|
+
|
67
|
+
##
|
68
|
+
# InsertError
|
69
|
+
class InsertError
|
70
|
+
attr_reader :row
|
71
|
+
attr_reader :errors
|
72
|
+
|
73
|
+
def initialize row, errors #:nodoc:
|
74
|
+
@row = row
|
75
|
+
@errors = errors
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,283 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright 2015 Google Inc. All rights reserved.
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
|
16
|
+
require "gcloud/bigquery/query_data"
|
17
|
+
require "gcloud/bigquery/job/list"
|
18
|
+
require "gcloud/bigquery/errors"
|
19
|
+
|
20
|
+
module Gcloud
|
21
|
+
module Bigquery
|
22
|
+
##
|
23
|
+
# = Job
|
24
|
+
#
|
25
|
+
# Represents a generic Job that may be performed on a Table.
|
26
|
+
#
|
27
|
+
# See {Managing Jobs, Datasets, and Projects
|
28
|
+
# }[https://cloud.google.com/bigquery/docs/managing_jobs_datasets_projects]
|
29
|
+
# for an overview of BigQuery jobs, and the {Jobs API
|
30
|
+
# reference}[https://cloud.google.com/bigquery/docs/reference/v2/jobs]
|
31
|
+
# for details.
|
32
|
+
#
|
33
|
+
# The subclasses of Job represent the specific BigQuery job types: CopyJob,
|
34
|
+
# ExtractJob, LoadJob, and QueryJob.
|
35
|
+
#
|
36
|
+
# A job instance is created when you call Project#query_job,
|
37
|
+
# Dataset#query_job, Table#copy, Table#extract, Table#load, or View#data.
|
38
|
+
#
|
39
|
+
# require "gcloud"
|
40
|
+
#
|
41
|
+
# gcloud = Gcloud.new
|
42
|
+
# bigquery = gcloud.bigquery
|
43
|
+
#
|
44
|
+
# q = "SELECT COUNT(word) as count FROM publicdata:samples.shakespeare"
|
45
|
+
# job = bigquery.query_job q
|
46
|
+
#
|
47
|
+
# loop do
|
48
|
+
# break if job.done?
|
49
|
+
# sleep 1
|
50
|
+
# job.refresh!
|
51
|
+
# end
|
52
|
+
#
|
53
|
+
# if job.failed?
|
54
|
+
# puts job.error
|
55
|
+
# else
|
56
|
+
# puts job.query_results.first
|
57
|
+
# end
|
58
|
+
#
|
59
|
+
class Job
|
60
|
+
##
|
61
|
+
# The Connection object.
|
62
|
+
attr_accessor :connection #:nodoc:
|
63
|
+
|
64
|
+
##
|
65
|
+
# The Google API Client object.
|
66
|
+
attr_accessor :gapi #:nodoc:
|
67
|
+
|
68
|
+
##
|
69
|
+
# Create an empty Job object.
|
70
|
+
def initialize #:nodoc:
|
71
|
+
@connection = nil
|
72
|
+
@gapi = {}
|
73
|
+
end
|
74
|
+
|
75
|
+
##
|
76
|
+
# The ID of the job.
|
77
|
+
def job_id
|
78
|
+
@gapi["jobReference"]["jobId"]
|
79
|
+
end
|
80
|
+
|
81
|
+
##
|
82
|
+
# The ID of the project containing the job.
|
83
|
+
def project_id
|
84
|
+
@gapi["jobReference"]["projectId"]
|
85
|
+
end
|
86
|
+
|
87
|
+
##
|
88
|
+
# The current state of the job. The possible values are +PENDING+,
|
89
|
+
# +RUNNING+, and +DONE+. A +DONE+ state does not mean that the job
|
90
|
+
# completed successfully. Use #failed? to discover if an error occurred
|
91
|
+
# or if the job was successful.
|
92
|
+
def state
|
93
|
+
return nil if @gapi["status"].nil?
|
94
|
+
@gapi["status"]["state"]
|
95
|
+
end
|
96
|
+
|
97
|
+
##
|
98
|
+
# Checks if the job's state is +RUNNING+.
|
99
|
+
def running?
|
100
|
+
return false if state.nil?
|
101
|
+
"running".casecmp(state).zero?
|
102
|
+
end
|
103
|
+
|
104
|
+
##
|
105
|
+
# Checks if the job's state is +PENDING+.
|
106
|
+
def pending?
|
107
|
+
return false if state.nil?
|
108
|
+
"pending".casecmp(state).zero?
|
109
|
+
end
|
110
|
+
|
111
|
+
##
|
112
|
+
# Checks if the job's state is +DONE+. When +true+, the job has stopped
|
113
|
+
# running. However, a +DONE+ state does not mean that the job completed
|
114
|
+
# successfully. Use #failed? to detect if an error occurred or if the
|
115
|
+
# job was successful.
|
116
|
+
def done?
|
117
|
+
return false if state.nil?
|
118
|
+
"done".casecmp(state).zero?
|
119
|
+
end
|
120
|
+
|
121
|
+
##
|
122
|
+
# Checks if an error is present.
|
123
|
+
def failed?
|
124
|
+
!error.nil?
|
125
|
+
end
|
126
|
+
|
127
|
+
##
|
128
|
+
# The time when the job was created.
|
129
|
+
def created_at
|
130
|
+
return nil if @gapi["statistics"].nil?
|
131
|
+
return nil if @gapi["statistics"]["creationTime"].nil?
|
132
|
+
Time.at(@gapi["statistics"]["creationTime"] / 1000.0)
|
133
|
+
end
|
134
|
+
|
135
|
+
##
|
136
|
+
# The time when the job was started.
|
137
|
+
# This field is present after the job's state changes from +PENDING+
|
138
|
+
# to either +RUNNING+ or +DONE+.
|
139
|
+
def started_at
|
140
|
+
return nil if @gapi["statistics"].nil?
|
141
|
+
return nil if @gapi["statistics"]["startTime"].nil?
|
142
|
+
Time.at(@gapi["statistics"]["startTime"] / 1000.0)
|
143
|
+
end
|
144
|
+
|
145
|
+
##
|
146
|
+
# The time when the job ended.
|
147
|
+
# This field is present when the job's state is +DONE+.
|
148
|
+
def ended_at
|
149
|
+
return nil if @gapi["statistics"].nil?
|
150
|
+
return nil if @gapi["statistics"]["endTime"].nil?
|
151
|
+
Time.at(@gapi["statistics"]["endTime"] / 1000.0)
|
152
|
+
end
|
153
|
+
|
154
|
+
##
|
155
|
+
# The configuration for the job. Returns a hash. See the {Jobs API
|
156
|
+
# reference}[https://cloud.google.com/bigquery/docs/reference/v2/jobs].
|
157
|
+
def configuration
|
158
|
+
hash = @gapi["configuration"] || {}
|
159
|
+
hash = hash.to_hash if hash.respond_to? :to_hash
|
160
|
+
hash
|
161
|
+
end
|
162
|
+
alias_method :config, :configuration
|
163
|
+
|
164
|
+
##
|
165
|
+
# The statistics for the job. Returns a hash. See the {Jobs API
|
166
|
+
# reference}[https://cloud.google.com/bigquery/docs/reference/v2/jobs].
|
167
|
+
def statistics
|
168
|
+
hash = @gapi["statistics"] || {}
|
169
|
+
hash = hash.to_hash if hash.respond_to? :to_hash
|
170
|
+
hash
|
171
|
+
end
|
172
|
+
alias_method :stats, :statistics
|
173
|
+
|
174
|
+
##
|
175
|
+
# The job's status. Returns a hash. The values contained in the hash are
|
176
|
+
# also exposed by #state, #error, and #errors.
|
177
|
+
def status
|
178
|
+
hash = @gapi["status"] || {}
|
179
|
+
hash = hash.to_hash if hash.respond_to? :to_hash
|
180
|
+
hash
|
181
|
+
end
|
182
|
+
|
183
|
+
##
|
184
|
+
# The last error for the job, if any errors have occurred. Returns a
|
185
|
+
# hash. See the {Jobs API
|
186
|
+
# reference}[https://cloud.google.com/bigquery/docs/reference/v2/jobs].
|
187
|
+
#
|
188
|
+
# === Returns
|
189
|
+
#
|
190
|
+
# +Hash+
|
191
|
+
#
|
192
|
+
# {
|
193
|
+
# "reason"=>"notFound",
|
194
|
+
# "message"=>"Not found: Table publicdata:samples.BAD_ID"
|
195
|
+
# }
|
196
|
+
#
|
197
|
+
def error
|
198
|
+
status["errorResult"]
|
199
|
+
end
|
200
|
+
|
201
|
+
##
|
202
|
+
# The errors for the job, if any errors have occurred. Returns an array
|
203
|
+
# of hash objects. See #error.
|
204
|
+
def errors
|
205
|
+
Array status["errors"]
|
206
|
+
end
|
207
|
+
|
208
|
+
##
|
209
|
+
# Created a new job with the current configuration.
|
210
|
+
def rerun!
|
211
|
+
ensure_connection!
|
212
|
+
resp = connection.insert_job configuration
|
213
|
+
if resp.success?
|
214
|
+
Job.from_gapi resp.data, connection
|
215
|
+
else
|
216
|
+
fail ApiError.from_response(resp)
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
##
|
221
|
+
# Reloads the job with current data from the BigQuery service.
|
222
|
+
def refresh!
|
223
|
+
ensure_connection!
|
224
|
+
resp = connection.get_job job_id
|
225
|
+
if resp.success?
|
226
|
+
@gapi = resp.data
|
227
|
+
else
|
228
|
+
fail ApiError.from_response(resp)
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
##
|
233
|
+
# New Job from a Google API Client object.
|
234
|
+
def self.from_gapi gapi, conn #:nodoc:
|
235
|
+
klass = klass_for gapi
|
236
|
+
klass.new.tap do |f|
|
237
|
+
f.gapi = gapi
|
238
|
+
f.connection = conn
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
protected
|
243
|
+
|
244
|
+
##
|
245
|
+
# Raise an error unless an active connection is available.
|
246
|
+
def ensure_connection!
|
247
|
+
fail "Must have active connection" unless connection
|
248
|
+
end
|
249
|
+
|
250
|
+
##
|
251
|
+
# Get the subclass for a job type
|
252
|
+
def self.klass_for gapi
|
253
|
+
if gapi["configuration"]["copy"]
|
254
|
+
return CopyJob
|
255
|
+
elsif gapi["configuration"]["extract"]
|
256
|
+
return ExtractJob
|
257
|
+
elsif gapi["configuration"]["load"]
|
258
|
+
return LoadJob
|
259
|
+
elsif gapi["configuration"]["query"]
|
260
|
+
return QueryJob
|
261
|
+
end
|
262
|
+
Job
|
263
|
+
end
|
264
|
+
|
265
|
+
def retrieve_table project_id, dataset_id, table_id
|
266
|
+
ensure_connection!
|
267
|
+
resp = connection.get_project_table project_id, dataset_id, table_id
|
268
|
+
if resp.success?
|
269
|
+
Table.from_gapi resp.data, connection
|
270
|
+
else
|
271
|
+
return nil if resp.status == 404
|
272
|
+
fail ApiError.from_response(resp)
|
273
|
+
end
|
274
|
+
end
|
275
|
+
end
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
# We need Job to be defined before loading these.
|
280
|
+
require "gcloud/bigquery/copy_job"
|
281
|
+
require "gcloud/bigquery/extract_job"
|
282
|
+
require "gcloud/bigquery/load_job"
|
283
|
+
require "gcloud/bigquery/query_job"
|