ghost_google-api-client 0.4.7.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. data/CHANGELOG.md +77 -0
  2. data/Gemfile +30 -0
  3. data/Gemfile.lock +80 -0
  4. data/LICENSE +202 -0
  5. data/README.md +71 -0
  6. data/Rakefile +42 -0
  7. data/bin/google-api +545 -0
  8. data/lib/compat/multi_json.rb +17 -0
  9. data/lib/google/api_client.rb +802 -0
  10. data/lib/google/api_client/batch.rb +296 -0
  11. data/lib/google/api_client/client_secrets.rb +106 -0
  12. data/lib/google/api_client/discovery.rb +19 -0
  13. data/lib/google/api_client/discovery/api.rb +287 -0
  14. data/lib/google/api_client/discovery/media.rb +77 -0
  15. data/lib/google/api_client/discovery/method.rb +369 -0
  16. data/lib/google/api_client/discovery/resource.rb +150 -0
  17. data/lib/google/api_client/discovery/schema.rb +119 -0
  18. data/lib/google/api_client/environment.rb +42 -0
  19. data/lib/google/api_client/errors.rb +49 -0
  20. data/lib/google/api_client/media.rb +172 -0
  21. data/lib/google/api_client/reference.rb +305 -0
  22. data/lib/google/api_client/result.rb +161 -0
  23. data/lib/google/api_client/service_account.rb +134 -0
  24. data/lib/google/api_client/version.rb +31 -0
  25. data/lib/google/inflection.rb +28 -0
  26. data/spec/fixtures/files/sample.txt +33 -0
  27. data/spec/google/api_client/batch_spec.rb +241 -0
  28. data/spec/google/api_client/discovery_spec.rb +670 -0
  29. data/spec/google/api_client/media_spec.rb +143 -0
  30. data/spec/google/api_client/result_spec.rb +185 -0
  31. data/spec/google/api_client/service_account_spec.rb +58 -0
  32. data/spec/google/api_client_spec.rb +139 -0
  33. data/spec/spec_helper.rb +7 -0
  34. data/tasks/gem.rake +97 -0
  35. data/tasks/git.rake +45 -0
  36. data/tasks/metrics.rake +22 -0
  37. data/tasks/spec.rake +57 -0
  38. data/tasks/wiki.rake +82 -0
  39. data/tasks/yard.rake +29 -0
  40. 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