Merge branch '18874-merge-wb2'
[arvados.git] / sdk / ruby-google-api-client / lib / google / api_client / service.rb
1 # Copyright 2013 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 'google/api_client'
16 require 'google/api_client/service/stub_generator'
17 require 'google/api_client/service/resource'
18 require 'google/api_client/service/request'
19 require 'google/api_client/service/result'
20 require 'google/api_client/service/batch'
21 require 'google/api_client/service/simple_file_store'
22
23 module Google
24   class APIClient
25
26     ##
27     # Experimental new programming interface at the API level.
28     # Hides Google::APIClient. Designed to be easier to use, with less code.
29     #
30     # @example
31     #   calendar = Google::APIClient::Service.new('calendar', 'v3')
32     #   result = calendar.events.list('calendarId' => 'primary').execute()
33     class Service
34       include Google::APIClient::Service::StubGenerator
35       extend Forwardable
36
37       DEFAULT_CACHE_FILE = 'discovery.cache'
38
39       # Cache for discovered APIs.
40       @@discovered = {}
41
42       ##
43       # Creates a new Service.
44       #
45       # @param [String, Symbol] api_name
46       #   The name of the API this service will access.
47       # @param [String, Symbol] api_version
48       #   The version of the API this service will access.
49       # @param [Hash] options
50       #   The configuration parameters for the service.
51       # @option options [Symbol, #generate_authenticated_request] :authorization
52       #   (:oauth_1)
53       #   The authorization mechanism used by the client.  The following
54       #   mechanisms are supported out-of-the-box:
55       #   <ul>
56       #     <li><code>:two_legged_oauth_1</code></li>
57       #     <li><code>:oauth_1</code></li>
58       #     <li><code>:oauth_2</code></li>
59       #   </ul>
60       # @option options [Boolean] :auto_refresh_token (true)
61       #   The setting that controls whether or not the api client attempts to
62       #   refresh authorization when a 401 is hit in #execute. If the token does
63       #   not support it, this option is ignored.
64       # @option options [String] :application_name
65       #   The name of the application using the client.
66       # @option options [String] :application_version
67       #   The version number of the application using the client.
68       # @option options [String] :host ("www.googleapis.com")
69       #   The API hostname used by the client. This rarely needs to be changed.
70       # @option options [String] :port (443)
71       #   The port number used by the client. This rarely needs to be changed.
72       # @option options [String] :discovery_path ("/discovery/v1")
73       #   The discovery base path. This rarely needs to be changed.
74       # @option options [String] :ca_file
75       #   Optional set of root certificates to use when validating SSL connections.
76       #   By default, a bundled set of trusted roots will be used.
77       # @option options [#generate_authenticated_request] :authorization
78       #   The authorization mechanism for requests. Used only if
79       #   `:authenticated` is `true`.
80       # @option options [TrueClass, FalseClass] :authenticated (default: true)
81       #   `true` if requests must be signed or somehow
82       #   authenticated, `false` otherwise.
83       # @option options [TrueClass, FalseClass] :gzip (default: true)
84       #   `true` if gzip enabled, `false` otherwise.
85       # @option options [Faraday::Connection] :connection
86       #   A custom connection to be used for all requests.
87       # @option options [ActiveSupport::Cache::Store, :default] :discovery_cache
88       #   A cache store to place the discovery documents for loaded APIs.
89       #   Avoids unnecessary roundtrips to the discovery service.
90       #   :default loads the default local file cache store.
91       def initialize(api_name, api_version, options = {})
92         @api_name = api_name.to_s
93         if api_version.nil?
94           raise ArgumentError,
95             "API version must be set"
96         end
97         @api_version = api_version.to_s
98         if options && !options.respond_to?(:to_hash)
99           raise ArgumentError,
100             "expected options Hash, got #{options.class}"
101         end
102
103         params = {}
104         [:application_name, :application_version, :authorization, :host, :port,
105          :discovery_path, :auto_refresh_token, :key, :user_ip,
106          :ca_file].each do |option|
107           if options.include? option
108             params[option] = options[option]
109           end
110         end
111
112         @client = Google::APIClient.new(params)
113
114         @connection = options[:connection] || @client.connection
115
116         @options = options
117
118         # Initialize cache store. Default to SimpleFileStore if :cache_store
119         # is not provided and we have write permissions.
120         if options.include? :cache_store
121           @cache_store = options[:cache_store]
122         else
123           cache_exists = File.exists?(DEFAULT_CACHE_FILE)
124           if (cache_exists && File.writable?(DEFAULT_CACHE_FILE)) ||
125              (!cache_exists && File.writable?(Dir.pwd))
126             @cache_store = Google::APIClient::Service::SimpleFileStore.new(
127               DEFAULT_CACHE_FILE)
128           end
129         end
130
131         # Attempt to read API definition from memory cache.
132         # Not thread-safe, but the worst that can happen is a cache miss.
133         unless @api = @@discovered[[api_name, api_version]]
134           # Attempt to read API definition from cache store, if there is one.
135           # If there's a miss or no cache store, call discovery service.
136           if !@cache_store.nil?
137             @api = @cache_store.fetch("%s/%s" % [api_name, api_version]) do
138               @client.discovered_api(api_name, api_version)
139             end
140           else
141             @api = @client.discovered_api(api_name, api_version)
142           end
143           @@discovered[[api_name, api_version]] = @api
144         end
145
146         generate_call_stubs(self, @api)
147       end
148
149       ##
150       # Returns the authorization mechanism used by the service.
151       #
152       # @return [#generate_authenticated_request] The authorization mechanism.
153       def_delegators :@client, :authorization, :authorization=
154
155       ##
156       # The setting that controls whether or not the service attempts to
157       # refresh authorization when a 401 is hit during an API call.
158       #
159       # @return [Boolean]
160       def_delegators :@client, :auto_refresh_token, :auto_refresh_token=
161
162       ##
163       # The application's API key issued by the API console.
164       #
165       # @return [String] The API key.
166       def_delegators :@client, :key, :key=
167
168       ##
169       # The Faraday/HTTP connection used by this service.
170       #
171       # @return [Faraday::Connection]
172       attr_accessor :connection
173
174       ##
175       # The cache store used for storing discovery documents.
176       #
177       # @return [ActiveSupport::Cache::Store,
178       #          Google::APIClient::Service::SimpleFileStore,
179       #          nil]
180       attr_reader :cache_store
181
182       ##
183       # Prepares a Google::APIClient::BatchRequest object to make batched calls.
184       # @param [Array] calls
185       #   Optional array of Google::APIClient::Service::Request to initialize
186       #   the batch request with.
187       # @param [Proc] block
188       #   Callback for every call's response. Won't be called if a call defined
189       #   a callback of its own.
190       #
191       # @yield [Google::APIClient::Service::Result]
192       #   block to be called when result ready
193       def batch(calls = nil, &block)
194         Google::APIClient::Service::BatchRequest.new(self, calls, &block)
195       end
196
197       ##
198       # Executes an API request.
199       # Do not call directly; this method is only used by Request objects when
200       # executing.
201       #
202       # @param [Google::APIClient::Service::Request,
203       #         Google::APIClient::Service::BatchCall] request
204       #   The request to be executed.
205       def execute(request)
206         if request.instance_of? Google::APIClient::Service::Request
207           params = {:api_method => request.method,
208             :parameters => request.parameters,
209             :connection => @connection}
210           if request.respond_to? :body
211             if request.body.respond_to? :to_hash
212               params[:body_object] = request.body
213             else
214               params[:body] = request.body
215             end
216           end
217           if request.respond_to? :media
218             params[:media] = request.media
219           end
220           [:authenticated, :gzip].each do |option|
221             if @options.include? option
222               params[option] = @options[option]
223             end
224           end
225           result = @client.execute(params)
226           return Google::APIClient::Service::Result.new(request, result)
227         elsif request.instance_of? Google::APIClient::Service::BatchRequest
228           @client.execute(request.base_batch, {:connection => @connection})
229         end
230       end
231     end
232   end
233 end