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,296 @@
|
|
1
|
+
# Copyright 2012 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
|
+
require 'addressable/uri'
|
16
|
+
require 'uuidtools'
|
17
|
+
|
18
|
+
module Google
|
19
|
+
class APIClient
|
20
|
+
|
21
|
+
# Helper class to contain a response to an individual batched call.
|
22
|
+
class BatchedCallResponse
|
23
|
+
attr_reader :call_id
|
24
|
+
attr_accessor :status, :headers, :body
|
25
|
+
|
26
|
+
def initialize(call_id, status = nil, headers = nil, body = nil)
|
27
|
+
@call_id, @status, @headers, @body = call_id, status, headers, body
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
##
|
32
|
+
# Wraps multiple API calls into a single over-the-wire HTTP request.
|
33
|
+
class BatchRequest
|
34
|
+
|
35
|
+
BATCH_BOUNDARY = "-----------RubyApiBatchRequest".freeze
|
36
|
+
|
37
|
+
attr_accessor :options
|
38
|
+
attr_reader :calls, :callbacks
|
39
|
+
|
40
|
+
##
|
41
|
+
# Creates a new batch request.
|
42
|
+
#
|
43
|
+
# @param [Hash] options
|
44
|
+
# Set of options for this request, the only important one being
|
45
|
+
# :connection, which specifies an HTTP connection to use.
|
46
|
+
# @param [Proc] block
|
47
|
+
# Callback for every call's response. Won't be called if a call defined
|
48
|
+
# a callback of its own.
|
49
|
+
#
|
50
|
+
# @return [Google::APIClient::BatchRequest] The constructed object.
|
51
|
+
def initialize(options = {}, &block)
|
52
|
+
# Request options, ignoring method and parameters.
|
53
|
+
@options = options
|
54
|
+
# Batched calls to be made, indexed by call ID.
|
55
|
+
@calls = {}
|
56
|
+
# Callbacks per batched call, indexed by call ID.
|
57
|
+
@callbacks = {}
|
58
|
+
# Order for the call IDs, since Ruby 1.8 hashes are unordered.
|
59
|
+
@order = []
|
60
|
+
# Global callback to be used for every call. If a specific callback
|
61
|
+
# has been defined for a request, this won't be called.
|
62
|
+
@global_callback = block if block_given?
|
63
|
+
# The last auto generated ID.
|
64
|
+
@last_auto_id = 0
|
65
|
+
# Base ID for the batch request.
|
66
|
+
@base_id = nil
|
67
|
+
end
|
68
|
+
|
69
|
+
##
|
70
|
+
# Add a new call to the batch request.
|
71
|
+
# Each call must have its own call ID; if not provided, one will
|
72
|
+
# automatically be generated, avoiding collisions. If duplicate call IDs
|
73
|
+
# are provided, an error will be thrown.
|
74
|
+
#
|
75
|
+
# @param [Hash, Google::APIClient::Reference] call: the call to be added.
|
76
|
+
# @param [String] call_id: the ID to be used for this call. Must be unique
|
77
|
+
# @param [Proc] block: callback for this call's response.
|
78
|
+
#
|
79
|
+
# @return [Google::APIClient::BatchRequest] The BatchRequest, for chaining
|
80
|
+
def add(call, call_id = nil, &block)
|
81
|
+
unless call.kind_of?(Google::APIClient::Reference)
|
82
|
+
call = Google::APIClient::Reference.new(call)
|
83
|
+
end
|
84
|
+
if call_id.nil?
|
85
|
+
call_id = new_id
|
86
|
+
end
|
87
|
+
if @calls.include?(call_id)
|
88
|
+
raise BatchError,
|
89
|
+
'A call with this ID already exists: %s' % call_id
|
90
|
+
end
|
91
|
+
@calls[call_id] = call
|
92
|
+
@order << call_id
|
93
|
+
if block_given?
|
94
|
+
@callbacks[call_id] = block
|
95
|
+
elsif @global_callback
|
96
|
+
@callbacks[call_id] = @global_callback
|
97
|
+
end
|
98
|
+
return self
|
99
|
+
end
|
100
|
+
|
101
|
+
##
|
102
|
+
# Convert this batch request into an HTTP request.
|
103
|
+
#
|
104
|
+
# @return [Array<String, String, Hash, String>]
|
105
|
+
# An array consisting of, in order: HTTP method, request path, request
|
106
|
+
# headers and request body.
|
107
|
+
def to_http_request
|
108
|
+
return ['POST', request_uri, request_headers, request_body]
|
109
|
+
end
|
110
|
+
|
111
|
+
##
|
112
|
+
# Processes the HTTP response to the batch request, issuing callbacks.
|
113
|
+
#
|
114
|
+
# @param [Faraday::Response] response: the HTTP response.
|
115
|
+
def process_response(response)
|
116
|
+
content_type = find_header('Content-Type', response.headers)
|
117
|
+
boundary = /.*boundary=(.+)/.match(content_type)[1]
|
118
|
+
parts = response.body.split(/--#{Regexp.escape(boundary)}/)
|
119
|
+
parts = parts[1...-1]
|
120
|
+
parts.each do |part|
|
121
|
+
call_response = deserialize_call_response(part)
|
122
|
+
callback = @callbacks[call_response.call_id]
|
123
|
+
call = @calls[call_response.call_id]
|
124
|
+
result = Google::APIClient::Result.new(call, nil, call_response)
|
125
|
+
callback.call(result) if callback
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
private
|
130
|
+
|
131
|
+
##
|
132
|
+
# Helper method to find a header from its name, regardless of case.
|
133
|
+
#
|
134
|
+
# @param [String] name: The name of the header to find.
|
135
|
+
# @param [Hash] headers: The hash of headers and their values.
|
136
|
+
#
|
137
|
+
# @return [String] The value of the desired header.
|
138
|
+
def find_header(name, headers)
|
139
|
+
_, header = headers.detect do |h, v|
|
140
|
+
h.downcase == name.downcase
|
141
|
+
end
|
142
|
+
return header
|
143
|
+
end
|
144
|
+
|
145
|
+
##
|
146
|
+
# Create a new call ID. Uses an auto-incrementing, conflict-avoiding ID.
|
147
|
+
#
|
148
|
+
# @return [String] the new, unique ID.
|
149
|
+
def new_id
|
150
|
+
@last_auto_id += 1
|
151
|
+
while @calls.include?(@last_auto_id)
|
152
|
+
@last_auto_id += 1
|
153
|
+
end
|
154
|
+
return @last_auto_id.to_s
|
155
|
+
end
|
156
|
+
|
157
|
+
##
|
158
|
+
# Convert an id to a Content-ID header value.
|
159
|
+
#
|
160
|
+
# @param [String] call_id: identifier of individual call.
|
161
|
+
#
|
162
|
+
# @return [String]
|
163
|
+
# A Content-ID header with the call_id encoded into it. A UUID is
|
164
|
+
# prepended to the value because Content-ID headers are supposed to be
|
165
|
+
# universally unique.
|
166
|
+
def id_to_header(call_id)
|
167
|
+
if @base_id.nil?
|
168
|
+
# TODO(sgomes): Use SecureRandom.uuid, drop UUIDTools when we drop 1.8
|
169
|
+
@base_id = UUIDTools::UUID.random_create.to_s
|
170
|
+
end
|
171
|
+
|
172
|
+
return '<%s+%s>' % [@base_id, Addressable::URI.encode(call_id)]
|
173
|
+
end
|
174
|
+
|
175
|
+
##
|
176
|
+
# Convert a Content-ID header value to an id. Presumes the Content-ID
|
177
|
+
# header conforms to the format that id_to_header() returns.
|
178
|
+
#
|
179
|
+
# @param [String] header: Content-ID header value.
|
180
|
+
#
|
181
|
+
# @return [String] The extracted ID value.
|
182
|
+
def header_to_id(header)
|
183
|
+
if !header.start_with?('<') || !header.end_with?('>') ||
|
184
|
+
!header.include?('+')
|
185
|
+
raise BatchError, 'Invalid value for Content-ID: "%s"' % header
|
186
|
+
end
|
187
|
+
|
188
|
+
base, call_id = header[1...-1].split('+')
|
189
|
+
return Addressable::URI.unencode(call_id)
|
190
|
+
end
|
191
|
+
|
192
|
+
##
|
193
|
+
# Convert a single batched call into a string.
|
194
|
+
#
|
195
|
+
# @param [Google::APIClient::Reference] call: the call to serialize.
|
196
|
+
#
|
197
|
+
# @return [String] The request as a string in application/http format.
|
198
|
+
def serialize_call(call)
|
199
|
+
http_request = call.to_request
|
200
|
+
method = http_request.method.to_s.upcase
|
201
|
+
path = http_request.path.to_s
|
202
|
+
status_line = method + " " + path + " HTTP/1.1"
|
203
|
+
serialized_call = status_line
|
204
|
+
if http_request.headers
|
205
|
+
http_request.headers.each do |header, value|
|
206
|
+
serialized_call << "\r\n%s: %s" % [header, value]
|
207
|
+
end
|
208
|
+
end
|
209
|
+
if http_request.body
|
210
|
+
serialized_call << "\r\n\r\n"
|
211
|
+
serialized_call << http_request.body
|
212
|
+
end
|
213
|
+
return serialized_call
|
214
|
+
end
|
215
|
+
|
216
|
+
##
|
217
|
+
# Auxiliary method to split the headers from the body in an HTTP response.
|
218
|
+
#
|
219
|
+
# @param [String] response: the response to parse.
|
220
|
+
#
|
221
|
+
# @return [Array<Hash>, String] The headers and the body, separately.
|
222
|
+
def split_headers_and_body(response)
|
223
|
+
headers = {}
|
224
|
+
payload = response.lstrip
|
225
|
+
while payload
|
226
|
+
line, payload = payload.split("\n", 2)
|
227
|
+
line.sub!(/\s+\z/, '')
|
228
|
+
break if line.empty?
|
229
|
+
match = /\A([^:]+):\s*/.match(line)
|
230
|
+
if match
|
231
|
+
headers[match[1]] = match.post_match
|
232
|
+
else
|
233
|
+
raise BatchError, 'Invalid header line in response: %s' % line
|
234
|
+
end
|
235
|
+
end
|
236
|
+
return headers, payload
|
237
|
+
end
|
238
|
+
|
239
|
+
##
|
240
|
+
# Convert a single batched response into a BatchedCallResponse object.
|
241
|
+
#
|
242
|
+
# @param [Google::APIClient::Reference] response:
|
243
|
+
# the request to deserialize.
|
244
|
+
#
|
245
|
+
# @return [BatchedCallResponse] The parsed and converted response.
|
246
|
+
def deserialize_call_response(call_response)
|
247
|
+
outer_headers, outer_body = split_headers_and_body(call_response)
|
248
|
+
status_line, payload = outer_body.split("\n", 2)
|
249
|
+
protocol, status, reason = status_line.split(' ', 3)
|
250
|
+
|
251
|
+
headers, body = split_headers_and_body(payload)
|
252
|
+
content_id = find_header('Content-ID', outer_headers)
|
253
|
+
call_id = header_to_id(content_id)
|
254
|
+
return BatchedCallResponse.new(call_id, status.to_i, headers, body)
|
255
|
+
end
|
256
|
+
|
257
|
+
##
|
258
|
+
# Return the request headers for the BatchRequest's HTTP request.
|
259
|
+
#
|
260
|
+
# @return [Hash] The HTTP headers.
|
261
|
+
def request_headers
|
262
|
+
return {
|
263
|
+
'Content-Type' => 'multipart/mixed; boundary=%s' % BATCH_BOUNDARY
|
264
|
+
}
|
265
|
+
end
|
266
|
+
|
267
|
+
##
|
268
|
+
# Return the request path for the BatchRequest's HTTP request.
|
269
|
+
#
|
270
|
+
# @return [String] The request path.
|
271
|
+
def request_uri
|
272
|
+
if @calls.nil? || @calls.empty?
|
273
|
+
raise BatchError, 'Cannot make an empty batch request'
|
274
|
+
end
|
275
|
+
# All APIs have the same batch path, so just get the first one.
|
276
|
+
return @calls.first[1].api_method.api.batch_path
|
277
|
+
end
|
278
|
+
|
279
|
+
##
|
280
|
+
# Return the request body for the BatchRequest's HTTP request.
|
281
|
+
#
|
282
|
+
# @return [String] The request body.
|
283
|
+
def request_body
|
284
|
+
body = ""
|
285
|
+
@order.each do |call_id|
|
286
|
+
body << "--" + BATCH_BOUNDARY + "\r\n"
|
287
|
+
body << "Content-Type: application/http\r\n"
|
288
|
+
body << "Content-ID: %s\r\n\r\n" % id_to_header(call_id)
|
289
|
+
body << serialize_call(@calls[call_id]) + "\r\n\r\n"
|
290
|
+
end
|
291
|
+
body << "--" + BATCH_BOUNDARY + "--"
|
292
|
+
return body
|
293
|
+
end
|
294
|
+
end
|
295
|
+
end
|
296
|
+
end
|
@@ -0,0 +1,106 @@
|
|
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 'multi_json'
|
17
|
+
require 'compat/multi_json'
|
18
|
+
|
19
|
+
|
20
|
+
module Google
|
21
|
+
class APIClient
|
22
|
+
##
|
23
|
+
# Manages the persistence of client configuration data and secrets.
|
24
|
+
class ClientSecrets
|
25
|
+
def self.load(filename=nil)
|
26
|
+
if filename && File.directory?(filename)
|
27
|
+
search_path = File.expand_path(filename)
|
28
|
+
filename = nil
|
29
|
+
end
|
30
|
+
while filename == nil
|
31
|
+
search_path ||= File.expand_path('.')
|
32
|
+
puts search_path
|
33
|
+
if File.exist?(File.join(search_path, 'client_secrets.json'))
|
34
|
+
filename = File.join(search_path, 'client_secrets.json')
|
35
|
+
elsif search_path == '/' || search_path =~ /[a-zA-Z]:[\/\\]/
|
36
|
+
raise ArgumentError,
|
37
|
+
'No client_secrets.json filename supplied ' +
|
38
|
+
'and/or could not be found in search path.'
|
39
|
+
else
|
40
|
+
search_path = File.expand_path(File.join(search_path, '..'))
|
41
|
+
end
|
42
|
+
end
|
43
|
+
data = File.open(filename, 'r') { |file| MultiJson.load(file.read) }
|
44
|
+
return self.new(data)
|
45
|
+
end
|
46
|
+
|
47
|
+
def initialize(options={})
|
48
|
+
# Client auth configuration
|
49
|
+
@flow = options[:flow] || options.keys.first.to_s || 'web'
|
50
|
+
fdata = options[@flow]
|
51
|
+
@client_id = fdata[:client_id] || fdata["client_id"]
|
52
|
+
@client_secret = fdata[:client_secret] || fdata["client_secret"]
|
53
|
+
@redirect_uris = fdata[:redirect_uris] || fdata["redirect_uris"]
|
54
|
+
@redirect_uris ||= [fdata[:redirect_uri]]
|
55
|
+
@javascript_origins = (
|
56
|
+
fdata[:javascript_origins] ||
|
57
|
+
fdata["javascript_origins"]
|
58
|
+
)
|
59
|
+
@javascript_origins ||= [fdata[:javascript_origin]]
|
60
|
+
@authorization_uri = fdata[:auth_uri] || fdata["auth_uri"]
|
61
|
+
@authorization_uri ||= fdata[:authorization_uri]
|
62
|
+
@token_credential_uri = fdata[:token_uri] || fdata["token_uri"]
|
63
|
+
@token_credential_uri ||= fdata[:token_credential_uri]
|
64
|
+
|
65
|
+
# Associated token info
|
66
|
+
@access_token = fdata[:access_token] || fdata["access_token"]
|
67
|
+
@refresh_token = fdata[:refresh_token] || fdata["refresh_token"]
|
68
|
+
@id_token = fdata[:id_token] || fdata["id_token"]
|
69
|
+
@expires_in = fdata[:expires_in] || fdata["expires_in"]
|
70
|
+
@expires_at = fdata[:expires_at] || fdata["expires_at"]
|
71
|
+
@issued_at = fdata[:issued_at] || fdata["issued_at"]
|
72
|
+
end
|
73
|
+
|
74
|
+
attr_reader(
|
75
|
+
:flow, :client_id, :client_secret, :redirect_uris, :javascript_origins,
|
76
|
+
:authorization_uri, :token_credential_uri, :access_token,
|
77
|
+
:refresh_token, :id_token, :expires_in, :expires_at, :issued_at
|
78
|
+
)
|
79
|
+
|
80
|
+
def to_json
|
81
|
+
return MultiJson.dump({
|
82
|
+
self.flow => ({
|
83
|
+
'client_id' => self.client_id,
|
84
|
+
'client_secret' => self.client_secret,
|
85
|
+
'redirect_uris' => self.redirect_uris,
|
86
|
+
'javascript_origins' => self.javascript_origins,
|
87
|
+
'auth_uri' => self.authorization_uri,
|
88
|
+
'token_uri' => self.token_credential_uri,
|
89
|
+
'access_token' => self.access_token,
|
90
|
+
'refresh_token' => self.refresh_token,
|
91
|
+
'id_token' => self.id_token,
|
92
|
+
'expires_in' => self.expires_in,
|
93
|
+
'expires_at' => self.expires_at,
|
94
|
+
'issued_at' => self.issued_at
|
95
|
+
}).inject({}) do |accu, (k, v)|
|
96
|
+
# Prunes empty values from JSON output.
|
97
|
+
unless v == nil || (v.respond_to?(:empty?) && v.empty?)
|
98
|
+
accu[k] = v
|
99
|
+
end
|
100
|
+
accu
|
101
|
+
end
|
102
|
+
})
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,19 @@
|
|
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 'google/api_client/discovery/api'
|
17
|
+
require 'google/api_client/discovery/resource'
|
18
|
+
require 'google/api_client/discovery/method'
|
19
|
+
require 'google/api_client/discovery/schema'
|
@@ -0,0 +1,287 @@
|
|
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 'addressable/uri'
|
17
|
+
|
18
|
+
require 'google/inflection'
|
19
|
+
require 'google/api_client/discovery/resource'
|
20
|
+
require 'google/api_client/discovery/method'
|
21
|
+
require 'google/api_client/discovery/media'
|
22
|
+
|
23
|
+
module Google
|
24
|
+
class APIClient
|
25
|
+
##
|
26
|
+
# A service that has been described by a discovery document.
|
27
|
+
class API
|
28
|
+
|
29
|
+
##
|
30
|
+
# Creates a description of a particular version of a service.
|
31
|
+
#
|
32
|
+
# @param [String] api
|
33
|
+
# The identifier for the service. Note that while this frequently
|
34
|
+
# matches the first segment of all of the service's RPC names, this
|
35
|
+
# should not be assumed. There is no requirement that these match.
|
36
|
+
# @param [String] version
|
37
|
+
# The identifier for the service version.
|
38
|
+
# @param [Hash] api_description
|
39
|
+
# The section of the discovery document that applies to this service
|
40
|
+
# version.
|
41
|
+
#
|
42
|
+
# @return [Google::APIClient::API] The constructed service object.
|
43
|
+
def initialize(document_base, discovery_document)
|
44
|
+
@document_base = Addressable::URI.parse(document_base)
|
45
|
+
@discovery_document = discovery_document
|
46
|
+
metaclass = (class << self; self; end)
|
47
|
+
self.discovered_resources.each do |resource|
|
48
|
+
method_name = Google::INFLECTOR.underscore(resource.name).to_sym
|
49
|
+
if !self.respond_to?(method_name)
|
50
|
+
metaclass.send(:define_method, method_name) { resource }
|
51
|
+
end
|
52
|
+
end
|
53
|
+
self.discovered_methods.each do |method|
|
54
|
+
method_name = Google::INFLECTOR.underscore(method.name).to_sym
|
55
|
+
if !self.respond_to?(method_name)
|
56
|
+
metaclass.send(:define_method, method_name) { method }
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
##
|
62
|
+
# Returns the id of the service.
|
63
|
+
#
|
64
|
+
# @return [String] The service id.
|
65
|
+
def id
|
66
|
+
return (
|
67
|
+
@discovery_document['id'] ||
|
68
|
+
"#{self.name}:#{self.version}"
|
69
|
+
)
|
70
|
+
end
|
71
|
+
|
72
|
+
##
|
73
|
+
# Returns the identifier for the service.
|
74
|
+
#
|
75
|
+
# @return [String] The service identifier.
|
76
|
+
def name
|
77
|
+
return @discovery_document['name']
|
78
|
+
end
|
79
|
+
|
80
|
+
##
|
81
|
+
# Returns the version of the service.
|
82
|
+
#
|
83
|
+
# @return [String] The service version.
|
84
|
+
def version
|
85
|
+
return @discovery_document['version']
|
86
|
+
end
|
87
|
+
|
88
|
+
##
|
89
|
+
# Returns a human-readable title for the API.
|
90
|
+
#
|
91
|
+
# @return [Hash] The API title.
|
92
|
+
def title
|
93
|
+
return @discovery_document['title']
|
94
|
+
end
|
95
|
+
|
96
|
+
##
|
97
|
+
# Returns a human-readable description of the API.
|
98
|
+
#
|
99
|
+
# @return [Hash] The API description.
|
100
|
+
def description
|
101
|
+
return @discovery_document['description']
|
102
|
+
end
|
103
|
+
|
104
|
+
##
|
105
|
+
# Returns a URI for the API documentation.
|
106
|
+
#
|
107
|
+
# @return [Hash] The API documentation.
|
108
|
+
def documentation
|
109
|
+
return Addressable::URI.parse(@discovery_document['documentationLink'])
|
110
|
+
end
|
111
|
+
|
112
|
+
##
|
113
|
+
# Returns true if this is the preferred version of this API.
|
114
|
+
#
|
115
|
+
# @return [TrueClass, FalseClass]
|
116
|
+
# Whether or not this is the preferred version of this API.
|
117
|
+
def preferred
|
118
|
+
return !!@discovery_document['preferred']
|
119
|
+
end
|
120
|
+
|
121
|
+
##
|
122
|
+
# Returns the list of API features.
|
123
|
+
#
|
124
|
+
# @return [Array]
|
125
|
+
# The features supported by this API.
|
126
|
+
def features
|
127
|
+
return @discovery_document['features'] || []
|
128
|
+
end
|
129
|
+
|
130
|
+
##
|
131
|
+
# Returns true if this API uses a data wrapper.
|
132
|
+
#
|
133
|
+
# @return [TrueClass, FalseClass]
|
134
|
+
# Whether or not this API uses a data wrapper.
|
135
|
+
def data_wrapper?
|
136
|
+
return self.features.include?('dataWrapper')
|
137
|
+
end
|
138
|
+
|
139
|
+
##
|
140
|
+
# Returns the base URI for the discovery document.
|
141
|
+
#
|
142
|
+
# @return [Addressable::URI] The base URI.
|
143
|
+
attr_reader :document_base
|
144
|
+
|
145
|
+
##
|
146
|
+
# Returns the base URI for this version of the service.
|
147
|
+
#
|
148
|
+
# @return [Addressable::URI] The base URI that methods are joined to.
|
149
|
+
def method_base
|
150
|
+
if @discovery_document['basePath']
|
151
|
+
return @method_base ||= (
|
152
|
+
self.document_base.join(Addressable::URI.parse(@discovery_document['basePath']))
|
153
|
+
).normalize
|
154
|
+
else
|
155
|
+
return nil
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
##
|
160
|
+
# Updates the hierarchy of resources and methods with the new base.
|
161
|
+
#
|
162
|
+
# @param [Addressable::URI, #to_str, String] new_base
|
163
|
+
# The new base URI to use for the service.
|
164
|
+
def method_base=(new_method_base)
|
165
|
+
@method_base = Addressable::URI.parse(new_method_base)
|
166
|
+
self.discovered_resources.each do |resource|
|
167
|
+
resource.method_base = @method_base
|
168
|
+
end
|
169
|
+
self.discovered_methods.each do |method|
|
170
|
+
method.method_base = @method_base
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
##
|
175
|
+
# Returns the base URI for batch calls to this service.
|
176
|
+
#
|
177
|
+
# @return [Addressable::URI] The base URI that methods are joined to.
|
178
|
+
def batch_path
|
179
|
+
if @discovery_document['batchPath']
|
180
|
+
return @batch_path ||= (
|
181
|
+
self.document_base.join(Addressable::URI.parse('/' +
|
182
|
+
@discovery_document['batchPath']))
|
183
|
+
).normalize
|
184
|
+
else
|
185
|
+
return nil
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
##
|
190
|
+
# A list of schemas available for this version of the API.
|
191
|
+
#
|
192
|
+
# @return [Hash] A list of {Google::APIClient::Schema} objects.
|
193
|
+
def schemas
|
194
|
+
return @schemas ||= (
|
195
|
+
(@discovery_document['schemas'] || []).inject({}) do |accu, (k, v)|
|
196
|
+
accu[k] = Google::APIClient::Schema.parse(self, v)
|
197
|
+
accu
|
198
|
+
end
|
199
|
+
)
|
200
|
+
end
|
201
|
+
|
202
|
+
##
|
203
|
+
# Returns a schema for a kind value.
|
204
|
+
#
|
205
|
+
# @return [Google::APIClient::Schema] The associated Schema object.
|
206
|
+
def schema_for_kind(kind)
|
207
|
+
api_name, schema_name = kind.split('#', 2)
|
208
|
+
if api_name != self.name
|
209
|
+
raise ArgumentError,
|
210
|
+
"The kind does not match this API. " +
|
211
|
+
"Expected '#{self.name}', got '#{api_name}'."
|
212
|
+
end
|
213
|
+
for k, v in self.schemas
|
214
|
+
return v if k.downcase == schema_name.downcase
|
215
|
+
end
|
216
|
+
return nil
|
217
|
+
end
|
218
|
+
|
219
|
+
##
|
220
|
+
# A list of resources available at the root level of this version of the
|
221
|
+
# API.
|
222
|
+
#
|
223
|
+
# @return [Array] A list of {Google::APIClient::Resource} objects.
|
224
|
+
def discovered_resources
|
225
|
+
return @discovered_resources ||= (
|
226
|
+
(@discovery_document['resources'] || []).inject([]) do |accu, (k, v)|
|
227
|
+
accu << Google::APIClient::Resource.new(
|
228
|
+
self, self.method_base, k, v
|
229
|
+
)
|
230
|
+
accu
|
231
|
+
end
|
232
|
+
)
|
233
|
+
end
|
234
|
+
|
235
|
+
##
|
236
|
+
# A list of methods available at the root level of this version of the
|
237
|
+
# API.
|
238
|
+
#
|
239
|
+
# @return [Array] A list of {Google::APIClient::Method} objects.
|
240
|
+
def discovered_methods
|
241
|
+
return @discovered_methods ||= (
|
242
|
+
(@discovery_document['methods'] || []).inject([]) do |accu, (k, v)|
|
243
|
+
accu << Google::APIClient::Method.new(self, self.method_base, k, v)
|
244
|
+
accu
|
245
|
+
end
|
246
|
+
)
|
247
|
+
end
|
248
|
+
|
249
|
+
##
|
250
|
+
# Allows deep inspection of the discovery document.
|
251
|
+
def [](key)
|
252
|
+
return @discovery_document[key]
|
253
|
+
end
|
254
|
+
|
255
|
+
##
|
256
|
+
# Converts the service to a flat mapping of RPC names and method objects.
|
257
|
+
#
|
258
|
+
# @return [Hash] All methods available on the service.
|
259
|
+
#
|
260
|
+
# @example
|
261
|
+
# # Discover available methods
|
262
|
+
# method_names = client.discovered_api('buzz').to_h.keys
|
263
|
+
def to_h
|
264
|
+
return @hash ||= (begin
|
265
|
+
methods_hash = {}
|
266
|
+
self.discovered_methods.each do |method|
|
267
|
+
methods_hash[method.id] = method
|
268
|
+
end
|
269
|
+
self.discovered_resources.each do |resource|
|
270
|
+
methods_hash.merge!(resource.to_h)
|
271
|
+
end
|
272
|
+
methods_hash
|
273
|
+
end)
|
274
|
+
end
|
275
|
+
|
276
|
+
##
|
277
|
+
# Returns a <code>String</code> representation of the service's state.
|
278
|
+
#
|
279
|
+
# @return [String] The service's state, as a <code>String</code>.
|
280
|
+
def inspect
|
281
|
+
sprintf(
|
282
|
+
"#<%s:%#0x ID:%s>", self.class.to_s, self.object_id, self.id
|
283
|
+
)
|
284
|
+
end
|
285
|
+
end
|
286
|
+
end
|
287
|
+
end
|