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.
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