ghost_google-api-client 0.4.7.1
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.
- data/CHANGELOG.md +77 -0
- data/Gemfile +30 -0
- data/Gemfile.lock +80 -0
- data/LICENSE +202 -0
- data/README.md +71 -0
- data/Rakefile +42 -0
- data/bin/google-api +545 -0
- data/lib/compat/multi_json.rb +17 -0
- data/lib/google/api_client.rb +802 -0
- data/lib/google/api_client/batch.rb +296 -0
- data/lib/google/api_client/client_secrets.rb +106 -0
- data/lib/google/api_client/discovery.rb +19 -0
- data/lib/google/api_client/discovery/api.rb +287 -0
- data/lib/google/api_client/discovery/media.rb +77 -0
- data/lib/google/api_client/discovery/method.rb +369 -0
- data/lib/google/api_client/discovery/resource.rb +150 -0
- data/lib/google/api_client/discovery/schema.rb +119 -0
- data/lib/google/api_client/environment.rb +42 -0
- data/lib/google/api_client/errors.rb +49 -0
- data/lib/google/api_client/media.rb +172 -0
- data/lib/google/api_client/reference.rb +305 -0
- data/lib/google/api_client/result.rb +161 -0
- data/lib/google/api_client/service_account.rb +134 -0
- data/lib/google/api_client/version.rb +31 -0
- data/lib/google/inflection.rb +28 -0
- data/spec/fixtures/files/sample.txt +33 -0
- data/spec/google/api_client/batch_spec.rb +241 -0
- data/spec/google/api_client/discovery_spec.rb +670 -0
- data/spec/google/api_client/media_spec.rb +143 -0
- data/spec/google/api_client/result_spec.rb +185 -0
- data/spec/google/api_client/service_account_spec.rb +58 -0
- data/spec/google/api_client_spec.rb +139 -0
- data/spec/spec_helper.rb +7 -0
- data/tasks/gem.rake +97 -0
- data/tasks/git.rake +45 -0
- data/tasks/metrics.rake +22 -0
- data/tasks/spec.rake +57 -0
- data/tasks/wiki.rake +82 -0
- data/tasks/yard.rake +29 -0
- metadata +253 -0
@@ -0,0 +1,119 @@
|
|
1
|
+
# Copyright 2010 Google Inc.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
|
16
|
+
require 'time'
|
17
|
+
require 'multi_json'
|
18
|
+
require 'compat/multi_json'
|
19
|
+
require 'base64'
|
20
|
+
require 'autoparse'
|
21
|
+
require 'addressable/uri'
|
22
|
+
require 'addressable/template'
|
23
|
+
|
24
|
+
require 'google/inflection'
|
25
|
+
require 'google/api_client/errors'
|
26
|
+
|
27
|
+
|
28
|
+
module Google
|
29
|
+
class APIClient
|
30
|
+
module Schema
|
31
|
+
def self.parse(api, schema_data)
|
32
|
+
# This method is super-long, but hard to break up due to the
|
33
|
+
# unavoidable dependence on closures and execution context.
|
34
|
+
schema_name = schema_data['id']
|
35
|
+
|
36
|
+
# Due to an oversight, schema IDs may not be URI references.
|
37
|
+
# TODO(bobaman): Remove this code once this has been resolved.
|
38
|
+
schema_uri = (
|
39
|
+
api.document_base +
|
40
|
+
(schema_name[0..0] != '#' ? '#' + schema_name : schema_name)
|
41
|
+
)
|
42
|
+
# puts schema_uri
|
43
|
+
|
44
|
+
# Due to an oversight, schema IDs may not be URI references.
|
45
|
+
# TODO(bobaman): Remove this whole lambda once this has been resolved.
|
46
|
+
reformat_references = lambda do |data|
|
47
|
+
# This code is not particularly efficient due to recursive traversal
|
48
|
+
# and excess object creation, but this hopefully shouldn't be an
|
49
|
+
# issue since it should only be called only once per schema per
|
50
|
+
# process.
|
51
|
+
if data.kind_of?(Hash) &&
|
52
|
+
data['$ref'] && !data['$ref'].kind_of?(Hash)
|
53
|
+
if data['$ref'].respond_to?(:to_str)
|
54
|
+
reference = data['$ref'].to_str
|
55
|
+
else
|
56
|
+
raise TypeError, "Expected String, got #{data['$ref'].class}"
|
57
|
+
end
|
58
|
+
reference = '#' + reference if reference[0..0] != '#'
|
59
|
+
data.merge({
|
60
|
+
'$ref' => reference
|
61
|
+
})
|
62
|
+
elsif data.kind_of?(Hash)
|
63
|
+
data.inject({}) do |accu, (key, value)|
|
64
|
+
if value.kind_of?(Hash)
|
65
|
+
accu[key] = reformat_references.call(value)
|
66
|
+
else
|
67
|
+
accu[key] = value
|
68
|
+
end
|
69
|
+
accu
|
70
|
+
end
|
71
|
+
else
|
72
|
+
data
|
73
|
+
end
|
74
|
+
end
|
75
|
+
schema_data = reformat_references.call(schema_data)
|
76
|
+
# puts schema_data.inspect
|
77
|
+
|
78
|
+
if schema_name
|
79
|
+
api_name_string =
|
80
|
+
Google::INFLECTOR.camelize(api.name)
|
81
|
+
api_version_string =
|
82
|
+
Google::INFLECTOR.camelize(api.version).gsub('.', '_')
|
83
|
+
# This is for compatibility with Ruby 1.8.7.
|
84
|
+
# TODO(bobaman) Remove this when we eventually stop supporting 1.8.7.
|
85
|
+
args = []
|
86
|
+
args << false if Class.method(:const_defined?).arity != 1
|
87
|
+
if Google::APIClient::Schema.const_defined?(api_name_string, *args)
|
88
|
+
api_name = Google::APIClient::Schema.const_get(
|
89
|
+
api_name_string, *args
|
90
|
+
)
|
91
|
+
else
|
92
|
+
api_name = Google::APIClient::Schema.const_set(
|
93
|
+
api_name_string, Module.new
|
94
|
+
)
|
95
|
+
end
|
96
|
+
if api_name.const_defined?(api_version_string, *args)
|
97
|
+
api_version = api_name.const_get(api_version_string, *args)
|
98
|
+
else
|
99
|
+
api_version = api_name.const_set(api_version_string, Module.new)
|
100
|
+
end
|
101
|
+
if api_version.const_defined?(schema_name, *args)
|
102
|
+
schema_class = api_version.const_get(schema_name, *args)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# It's possible the schema has already been defined. If so, don't
|
107
|
+
# redefine it. This means that reloading a schema which has already
|
108
|
+
# been loaded into memory is not possible.
|
109
|
+
unless schema_class
|
110
|
+
schema_class = AutoParse.generate(schema_data, :uri => schema_uri)
|
111
|
+
if schema_name
|
112
|
+
api_version.const_set(schema_name, schema_class)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
return schema_class
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# Copyright 2010 Google Inc.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
|
16
|
+
module Google
|
17
|
+
class APIClient
|
18
|
+
module ENV
|
19
|
+
OS_VERSION = begin
|
20
|
+
if RUBY_PLATFORM =~ /mswin|win32|mingw|bccwin|cygwin/
|
21
|
+
# TODO(bobaman)
|
22
|
+
# Confirm that all of these Windows environments actually have access
|
23
|
+
# to the `ver` command.
|
24
|
+
`ver`.sub(/\s*\[Version\s*/, '/').sub(']', '').strip
|
25
|
+
elsif RUBY_PLATFORM =~ /darwin/i
|
26
|
+
"Mac OS X/#{`sw_vers -productVersion`}"
|
27
|
+
elsif RUBY_PLATFORM == 'java'
|
28
|
+
# Get the information from java system properties to avoid spawning a
|
29
|
+
# sub-process, which is not friendly in some contexts (web servers).
|
30
|
+
require 'java'
|
31
|
+
name = java.lang.System.getProperty('os.name')
|
32
|
+
version = java.lang.System.getProperty('os.version')
|
33
|
+
"#{name}/#{version}"
|
34
|
+
else
|
35
|
+
`uname -sr`.sub(' ', '/')
|
36
|
+
end
|
37
|
+
rescue Exception
|
38
|
+
RUBY_PLATFORM
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# Copyright 2010 Google Inc.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
|
16
|
+
module Google
|
17
|
+
class APIClient
|
18
|
+
##
|
19
|
+
# An error which is raised when there is an unexpected response or other
|
20
|
+
# transport error that prevents an operation from succeeding.
|
21
|
+
class TransmissionError < StandardError
|
22
|
+
end
|
23
|
+
|
24
|
+
##
|
25
|
+
# An exception that is raised if a method is called with missing or
|
26
|
+
# invalid parameter values.
|
27
|
+
class ValidationError < StandardError
|
28
|
+
end
|
29
|
+
|
30
|
+
##
|
31
|
+
# A 4xx class HTTP error occurred.
|
32
|
+
class ClientError < TransmissionError
|
33
|
+
end
|
34
|
+
|
35
|
+
##
|
36
|
+
# A 5xx class HTTP error occurred.
|
37
|
+
class ServerError < TransmissionError
|
38
|
+
end
|
39
|
+
|
40
|
+
##
|
41
|
+
# An exception that is raised if an ID token could not be validated.
|
42
|
+
class InvalidIDTokenError < StandardError
|
43
|
+
end
|
44
|
+
|
45
|
+
# Error class for problems in batch requests.
|
46
|
+
class BatchError < StandardError
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,172 @@
|
|
1
|
+
# Copyright 2010 Google Inc.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
module Google
|
16
|
+
class APIClient
|
17
|
+
##
|
18
|
+
# Uploadable media support. Holds an IO stream & content type.
|
19
|
+
#
|
20
|
+
# @see Faraday::UploadIO
|
21
|
+
# @example
|
22
|
+
# media = Google::APIClient::UploadIO.new('mymovie.m4v', 'video/mp4')
|
23
|
+
class UploadIO < Faraday::UploadIO
|
24
|
+
##
|
25
|
+
# Get the length of the stream
|
26
|
+
# @return [Integer]
|
27
|
+
# Length of stream, in bytes
|
28
|
+
def length
|
29
|
+
io.respond_to?(:length) ? io.length : File.size(local_path)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
##
|
34
|
+
# Resumable uploader.
|
35
|
+
#
|
36
|
+
class ResumableUpload
|
37
|
+
attr_reader :result
|
38
|
+
attr_accessor :client
|
39
|
+
attr_accessor :chunk_size
|
40
|
+
attr_accessor :media
|
41
|
+
attr_accessor :location
|
42
|
+
|
43
|
+
##
|
44
|
+
# Creates a new uploader.
|
45
|
+
#
|
46
|
+
# @param [Google::APIClient::Result] result
|
47
|
+
# Result of the initial request that started the upload
|
48
|
+
# @param [Google::APIClient::UploadIO] media
|
49
|
+
# Media to upload
|
50
|
+
# @param [String] location
|
51
|
+
# URL to upload to
|
52
|
+
def initialize(result, media, location)
|
53
|
+
self.media = media
|
54
|
+
self.location = location
|
55
|
+
self.chunk_size = 256 * 1024
|
56
|
+
|
57
|
+
@api_method = result.reference.api_method
|
58
|
+
@result = result
|
59
|
+
@offset = 0
|
60
|
+
@complete = false
|
61
|
+
end
|
62
|
+
|
63
|
+
##
|
64
|
+
# Sends all remaining chunks to the server
|
65
|
+
#
|
66
|
+
# @param [Google::APIClient] api_client
|
67
|
+
# API Client instance to use for sending
|
68
|
+
def send_all(api_client)
|
69
|
+
until complete?
|
70
|
+
send_chunk(api_client)
|
71
|
+
break unless result.status == 308
|
72
|
+
end
|
73
|
+
return result
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
##
|
78
|
+
# Sends the next chunk to the server
|
79
|
+
#
|
80
|
+
# @param [Google::APIClient] api_client
|
81
|
+
# API Client instance to use for sending
|
82
|
+
def send_chunk(api_client)
|
83
|
+
if @offset.nil?
|
84
|
+
return resync_range(api_client)
|
85
|
+
end
|
86
|
+
|
87
|
+
start_offset = @offset
|
88
|
+
self.media.io.pos = start_offset
|
89
|
+
chunk = self.media.io.read(chunk_size)
|
90
|
+
content_length = chunk.bytesize
|
91
|
+
|
92
|
+
end_offset = start_offset + content_length - 1
|
93
|
+
@result = api_client.execute(
|
94
|
+
:uri => self.location,
|
95
|
+
:http_method => :put,
|
96
|
+
:headers => {
|
97
|
+
'Content-Length' => "#{content_length}",
|
98
|
+
'Content-Type' => self.media.content_type,
|
99
|
+
'Content-Range' => "bytes #{start_offset}-#{end_offset}/#{media.length}" },
|
100
|
+
:body => chunk)
|
101
|
+
return process_result(@result)
|
102
|
+
end
|
103
|
+
|
104
|
+
##
|
105
|
+
# Check if upload is complete
|
106
|
+
#
|
107
|
+
# @return [TrueClass, FalseClass]
|
108
|
+
# Whether or not the upload complete successfully
|
109
|
+
def complete?
|
110
|
+
return @complete
|
111
|
+
end
|
112
|
+
|
113
|
+
##
|
114
|
+
# Check if the upload URL expired (upload not completed in alotted time.)
|
115
|
+
# Expired uploads must be restarted from the beginning
|
116
|
+
#
|
117
|
+
# @return [TrueClass, FalseClass]
|
118
|
+
# Whether or not the upload has expired and can not be resumed
|
119
|
+
def expired?
|
120
|
+
return @result.status == 404 || @result.status == 410
|
121
|
+
end
|
122
|
+
|
123
|
+
##
|
124
|
+
# Get the last saved range from the server in case an error occurred
|
125
|
+
# and the offset is not known.
|
126
|
+
#
|
127
|
+
# @param [Google::APIClient] api_client
|
128
|
+
# API Client instance to use for sending
|
129
|
+
def resync_range(api_client)
|
130
|
+
r = api_client.execute(
|
131
|
+
:uri => self.location,
|
132
|
+
:http_method => :put,
|
133
|
+
:headers => {
|
134
|
+
'Content-Length' => "0",
|
135
|
+
'Content-Range' => "bytes */#{media.length}" })
|
136
|
+
return process_result(r)
|
137
|
+
end
|
138
|
+
|
139
|
+
##
|
140
|
+
# Check the result from the server, updating the offset and/or location
|
141
|
+
# if available.
|
142
|
+
#
|
143
|
+
# @param [Google::APIClient::Result] r
|
144
|
+
# Result of a chunk upload or range query
|
145
|
+
def process_result(result)
|
146
|
+
case result.status
|
147
|
+
when 200...299
|
148
|
+
@complete = true
|
149
|
+
if @api_method
|
150
|
+
# Inject the original API method so data is parsed correctly
|
151
|
+
result.reference.api_method = @api_method
|
152
|
+
end
|
153
|
+
return result
|
154
|
+
when 308
|
155
|
+
range = result.headers['range']
|
156
|
+
if range
|
157
|
+
@offset = range.scan(/\d+/).collect{|x| Integer(x)}.last + 1
|
158
|
+
end
|
159
|
+
if result.headers['location']
|
160
|
+
self.location = result.headers['location']
|
161
|
+
end
|
162
|
+
when 500...599
|
163
|
+
# Invalidate the offset to mark it needs to be queried on the
|
164
|
+
# next request
|
165
|
+
@offset = nil
|
166
|
+
end
|
167
|
+
return nil
|
168
|
+
end
|
169
|
+
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
@@ -0,0 +1,305 @@
|
|
1
|
+
# Copyright 2010 Google Inc.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
|
16
|
+
gem 'faraday', '~> 0.8.1'
|
17
|
+
require 'faraday'
|
18
|
+
require 'faraday/utils'
|
19
|
+
require 'multi_json'
|
20
|
+
require 'compat/multi_json'
|
21
|
+
require 'addressable/uri'
|
22
|
+
require 'stringio'
|
23
|
+
require 'google/api_client/discovery'
|
24
|
+
|
25
|
+
# TODO - needs some serious cleanup
|
26
|
+
|
27
|
+
module Google
|
28
|
+
class APIClient
|
29
|
+
class Reference
|
30
|
+
MULTIPART_BOUNDARY = "-----------RubyApiMultipartPost".freeze
|
31
|
+
|
32
|
+
def initialize(options={})
|
33
|
+
# We only need this to do lookups on method ID String values
|
34
|
+
# It's optional, but method ID lookups will fail if the client is
|
35
|
+
# omitted.
|
36
|
+
@client = options[:client]
|
37
|
+
@version = options[:version] || 'v1'
|
38
|
+
|
39
|
+
self.connection = options[:connection] || Faraday.default_connection
|
40
|
+
self.authorization = options[:authorization]
|
41
|
+
self.api_method = options[:api_method]
|
42
|
+
self.parameters = options[:parameters] || {}
|
43
|
+
# These parameters are handled differently because they're not
|
44
|
+
# parameters to the API method, but rather to the API system.
|
45
|
+
if self.parameters.kind_of?(Array)
|
46
|
+
if options[:key]
|
47
|
+
self.parameters.reject! { |k, _| k == 'key' }
|
48
|
+
self.parameters << ['key', options[:key]]
|
49
|
+
end
|
50
|
+
if options[:user_ip]
|
51
|
+
self.parameters.reject! { |k, _| k == 'userIp' }
|
52
|
+
self.parameters << ['userIp', options[:user_ip]]
|
53
|
+
end
|
54
|
+
elsif self.parameters.kind_of?(Hash)
|
55
|
+
self.parameters['key'] ||= options[:key] if options[:key]
|
56
|
+
self.parameters['userIp'] ||= options[:user_ip] if options[:user_ip]
|
57
|
+
# Convert to Array, because they're easier to work with when
|
58
|
+
# repeated parameters are an issue.
|
59
|
+
self.parameters = self.parameters.to_a
|
60
|
+
else
|
61
|
+
raise TypeError,
|
62
|
+
"Expected Array or Hash, got #{self.parameters.class}."
|
63
|
+
end
|
64
|
+
self.headers = options[:headers] || {}
|
65
|
+
if options[:media]
|
66
|
+
self.media = options[:media]
|
67
|
+
upload_type = self.parameters.find { |(k, _)| ['uploadType', 'upload_type'].include?(k) }.last
|
68
|
+
case upload_type
|
69
|
+
when "media"
|
70
|
+
if options[:body] || options[:body_object]
|
71
|
+
raise ArgumentError,
|
72
|
+
"Can not specify body & body object for simple uploads."
|
73
|
+
end
|
74
|
+
self.headers['Content-Type'] ||= self.media.content_type
|
75
|
+
self.body = self.media
|
76
|
+
when "multipart"
|
77
|
+
unless options[:body_object]
|
78
|
+
raise ArgumentError, "Multipart requested but no body object."
|
79
|
+
end
|
80
|
+
# This is all a bit of a hack due to Signet requiring body to be a
|
81
|
+
# string. Ideally, update Signet to delay serialization so we can
|
82
|
+
# just pass streams all the way down through to the HTTP library.
|
83
|
+
metadata = StringIO.new(serialize_body(options[:body_object]))
|
84
|
+
env = {
|
85
|
+
:request_headers => {
|
86
|
+
'Content-Type' =>
|
87
|
+
"multipart/related;boundary=#{MULTIPART_BOUNDARY}"
|
88
|
+
},
|
89
|
+
:request => {:boundary => MULTIPART_BOUNDARY}
|
90
|
+
}
|
91
|
+
multipart = Faraday::Request::Multipart.new
|
92
|
+
self.body = multipart.create_multipart(env, [
|
93
|
+
[nil, Faraday::UploadIO.new(
|
94
|
+
metadata, 'application/json', 'file.json'
|
95
|
+
)],
|
96
|
+
[nil, self.media]])
|
97
|
+
self.headers.update(env[:request_headers])
|
98
|
+
when "resumable"
|
99
|
+
file_length = self.media.length
|
100
|
+
self.headers['X-Upload-Content-Type'] = self.media.content_type
|
101
|
+
self.headers['X-Upload-Content-Length'] = file_length.to_s
|
102
|
+
if options[:body_object]
|
103
|
+
self.headers['Content-Type'] ||= 'application/json'
|
104
|
+
self.body = serialize_body(options[:body_object])
|
105
|
+
else
|
106
|
+
self.body = ''
|
107
|
+
end
|
108
|
+
else
|
109
|
+
raise ArgumentError, "Invalid uploadType for media."
|
110
|
+
end
|
111
|
+
elsif options[:body]
|
112
|
+
self.body = options[:body]
|
113
|
+
elsif options[:body_object]
|
114
|
+
self.headers['Content-Type'] ||= 'application/json'
|
115
|
+
self.body = serialize_body(options[:body_object])
|
116
|
+
else
|
117
|
+
self.body = ''
|
118
|
+
end
|
119
|
+
unless self.api_method
|
120
|
+
self.http_method = options[:http_method] || 'GET'
|
121
|
+
self.uri = options[:uri]
|
122
|
+
unless self.parameters.empty?
|
123
|
+
query_values = (self.uri.query_values(Array) || [])
|
124
|
+
self.uri.query = Addressable::URI.form_encode(
|
125
|
+
(query_values + self.parameters).sort
|
126
|
+
)
|
127
|
+
self.uri.query = nil if self.uri.query == ""
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def serialize_body(body)
|
133
|
+
return body.to_json if body.respond_to?(:to_json)
|
134
|
+
return MultiJson.dump(options[:body_object].to_hash) if body.respond_to?(:to_hash)
|
135
|
+
raise TypeError, 'Could not convert body object to JSON.' +
|
136
|
+
'Must respond to :to_json or :to_hash.'
|
137
|
+
end
|
138
|
+
|
139
|
+
def media
|
140
|
+
return @media
|
141
|
+
end
|
142
|
+
|
143
|
+
def media=(media)
|
144
|
+
@media = (media)
|
145
|
+
end
|
146
|
+
|
147
|
+
def authorization
|
148
|
+
return @authorization
|
149
|
+
end
|
150
|
+
|
151
|
+
def authorization=(new_authorization)
|
152
|
+
@authorization = new_authorization
|
153
|
+
end
|
154
|
+
|
155
|
+
def connection
|
156
|
+
return @connection
|
157
|
+
end
|
158
|
+
|
159
|
+
def connection=(new_connection)
|
160
|
+
if new_connection.kind_of?(Faraday::Connection)
|
161
|
+
@connection = new_connection
|
162
|
+
else
|
163
|
+
raise TypeError,
|
164
|
+
"Expected Faraday::Connection, got #{new_connection.class}."
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
def api_method
|
169
|
+
return @api_method
|
170
|
+
end
|
171
|
+
|
172
|
+
def api_method=(new_api_method)
|
173
|
+
if new_api_method.kind_of?(Google::APIClient::Method) ||
|
174
|
+
new_api_method == nil
|
175
|
+
@api_method = new_api_method
|
176
|
+
elsif new_api_method.respond_to?(:to_str) ||
|
177
|
+
new_api_method.kind_of?(Symbol)
|
178
|
+
unless @client
|
179
|
+
raise ArgumentError,
|
180
|
+
"API method lookup impossible without client instance."
|
181
|
+
end
|
182
|
+
new_api_method = new_api_method.to_s
|
183
|
+
# This method of guessing the API is unreliable. This will fail for
|
184
|
+
# APIs where the first segment of the RPC name does not match the
|
185
|
+
# service name. However, this is a fallback mechanism anyway.
|
186
|
+
# Developers should be passing in a reference to the method, rather
|
187
|
+
# than passing in a string or symbol. This should raise an error
|
188
|
+
# in the case of a mismatch.
|
189
|
+
api = new_api_method[/^([^.]+)\./, 1]
|
190
|
+
@api_method = @client.discovered_method(
|
191
|
+
new_api_method, api, @version
|
192
|
+
)
|
193
|
+
if @api_method
|
194
|
+
# Ditch the client reference, we won't need it again.
|
195
|
+
@client = nil
|
196
|
+
else
|
197
|
+
raise ArgumentError, "API method could not be found."
|
198
|
+
end
|
199
|
+
else
|
200
|
+
raise TypeError,
|
201
|
+
"Expected Google::APIClient::Method, got #{new_api_method.class}."
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
def parameters
|
206
|
+
return @parameters
|
207
|
+
end
|
208
|
+
|
209
|
+
def parameters=(new_parameters)
|
210
|
+
# No type-checking needed, the Method class handles this.
|
211
|
+
@parameters = new_parameters
|
212
|
+
end
|
213
|
+
|
214
|
+
def body
|
215
|
+
return @body
|
216
|
+
end
|
217
|
+
|
218
|
+
def body=(new_body)
|
219
|
+
if new_body.respond_to?(:to_str)
|
220
|
+
@body = new_body.to_str
|
221
|
+
elsif new_body.respond_to?(:read)
|
222
|
+
@body = new_body.read()
|
223
|
+
elsif new_body.respond_to?(:inject)
|
224
|
+
@body = (new_body.inject(StringIO.new) do |accu, chunk|
|
225
|
+
accu.write(chunk)
|
226
|
+
accu
|
227
|
+
end).string
|
228
|
+
else
|
229
|
+
raise TypeError,
|
230
|
+
"Expected body to be String, IO, or Enumerable chunks."
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
def headers
|
235
|
+
return @headers ||= {}
|
236
|
+
end
|
237
|
+
|
238
|
+
def headers=(new_headers)
|
239
|
+
if new_headers.kind_of?(Array) || new_headers.kind_of?(Hash)
|
240
|
+
@headers = new_headers
|
241
|
+
else
|
242
|
+
raise TypeError, "Expected Hash or Array, got #{new_headers.class}."
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
def http_method
|
247
|
+
return @http_method ||= self.api_method.http_method
|
248
|
+
end
|
249
|
+
|
250
|
+
def http_method=(new_http_method)
|
251
|
+
if new_http_method.kind_of?(Symbol)
|
252
|
+
@http_method = new_http_method.to_s.upcase
|
253
|
+
elsif new_http_method.respond_to?(:to_str)
|
254
|
+
@http_method = new_http_method.to_str.upcase
|
255
|
+
else
|
256
|
+
raise TypeError,
|
257
|
+
"Expected String or Symbol, got #{new_http_method.class}."
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
def uri
|
262
|
+
return @uri ||= self.api_method.generate_uri(self.parameters)
|
263
|
+
end
|
264
|
+
|
265
|
+
def uri=(new_uri)
|
266
|
+
@uri = Addressable::URI.parse(new_uri)
|
267
|
+
end
|
268
|
+
|
269
|
+
def to_request
|
270
|
+
if self.api_method
|
271
|
+
return self.api_method.generate_request(
|
272
|
+
self.parameters, self.body, self.headers,
|
273
|
+
:connection => self.connection
|
274
|
+
)
|
275
|
+
else
|
276
|
+
return self.connection.build_request(
|
277
|
+
self.http_method.to_s.downcase.to_sym
|
278
|
+
) do |req|
|
279
|
+
req.url(Addressable::URI.parse(self.uri).normalize.to_s)
|
280
|
+
req.headers = Faraday::Utils::Headers.new(self.headers)
|
281
|
+
req.body = self.body
|
282
|
+
end
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
def to_hash
|
287
|
+
options = {}
|
288
|
+
if self.api_method
|
289
|
+
options[:api_method] = self.api_method
|
290
|
+
options[:parameters] = self.parameters
|
291
|
+
else
|
292
|
+
options[:http_method] = self.http_method
|
293
|
+
options[:uri] = self.uri
|
294
|
+
end
|
295
|
+
options[:headers] = self.headers
|
296
|
+
options[:body] = self.body
|
297
|
+
options[:connection] = self.connection
|
298
|
+
unless self.authorization.nil?
|
299
|
+
options[:authorization] = self.authorization
|
300
|
+
end
|
301
|
+
return options
|
302
|
+
end
|
303
|
+
end
|
304
|
+
end
|
305
|
+
end
|