Class: Aspera::Rest::Client
- Inherits:
-
Object
- Object
- Aspera::Rest::Client
- Defined in:
- lib/aspera/rest/client.rb
Overview
Make HTTP calls, equivalent to rest-client rest call errors are raised as exception CallError and error are analyzed in ErrorAnalyzer
Direct Known Subclasses
Api::Alee, Api::AoC, Api::Ats, Api::Faspex, Api::Httpgw, Api::Node
Instance Attribute Summary collapse
-
#auth_params ⇒ Object
readonly
All original constructor parameters.
-
#base_url ⇒ Object
readonly
The root URL for the API.
-
#headers ⇒ Object
readonly
Base common headers of API.
CRUD collapse
-
#cancel(subpath, **kwargs) ⇒ Object
CANCEL. -
#create(subpath, params, **kwargs) ⇒ Object
POSTJSON body. -
#delete(subpath, params = nil, **kwargs) ⇒ Object
DELETE. -
#read(subpath, query = nil, **kwargs) ⇒ Object
GET. -
#update(subpath, params, **kwargs) ⇒ Object
PUTJSON body.
Instance Method Summary collapse
-
#call(operation:, subpath: nil, query: nil, content_type: nil, body: nil, headers: nil, save_to: nil, exception: true, ret: :data) ⇒ Array(Hash, Net::HTTPResponse), ...
HTTP/S REST call.
-
#initialize(base_url:, auth: {type: :none}, not_auth_codes: ['401'], redirect_max: 0, headers: {}) ⇒ Client
constructor
Create a REST object for API calls HTTP sessions parameters can be modified using global parameters in Parameters For example, TLS verification can be skipped.
-
#oauth ⇒ OAuth::Base
OAuth object used for authorization, when auth type is
:oauth2. -
#params ⇒ Hash
Parameters to create a copy of this object, e.g.
Constructor Details
#initialize(base_url:, auth: {type: :none}, not_auth_codes: ['401'], redirect_max: 0, headers: {}) ⇒ Client
Create a REST object for API calls HTTP sessions parameters can be modified using global parameters in Parameters For example, TLS verification can be skipped.
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
# File 'lib/aspera/rest/client.rb', line 81 def initialize( base_url:, auth: {type: :none}, not_auth_codes: ['401'], redirect_max: 0, headers: {} ) Aspera.assert_type(base_url, String) # base url with no trailing slashes (note: string may be frozen) @base_url = base_url.chomp('/') # remove trailing port if it is 443 and scheme is https @base_url = @base_url.gsub(/:443$/, '') if @base_url.start_with?('https://') @base_url = @base_url.gsub(/:80$/, '') if @base_url.start_with?('http://') Log.log.debug { "Client.new(#{@base_url})" } # default is no auth @auth_params = auth Aspera.assert_type(@auth_params, Hash) Aspera.assert(@auth_params.key?(:type), 'no auth type defined') @not_auth_codes = not_auth_codes Aspera.assert_type(@not_auth_codes, Array) # persistent session @http_session = nil @redirect_max = redirect_max Aspera.assert_type(@redirect_max, Integer) @headers = headers.clone Aspera.assert_type(@headers, Hash) @headers['User-Agent'] ||= Parameters.instance.user_agent # OAuth object (created on demand) @oauth = nil end |
Instance Attribute Details
#auth_params ⇒ Object (readonly)
All original constructor parameters
48 49 50 |
# File 'lib/aspera/rest/client.rb', line 48 def auth_params @auth_params end |
#base_url ⇒ Object (readonly)
The root URL for the API
51 52 53 |
# File 'lib/aspera/rest/client.rb', line 51 def base_url @base_url end |
#headers ⇒ Object (readonly)
Base common headers of API
54 55 56 |
# File 'lib/aspera/rest/client.rb', line 54 def headers @headers end |
Instance Method Details
#call(operation:, subpath: nil, query: nil, content_type: nil, body: nil, headers: nil, save_to: nil, exception: true, ret: :data) ⇒ Array(Hash, Net::HTTPResponse), ...
HTTP/S REST call
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 |
# File 'lib/aspera/rest/client.rb', line 138 def call( operation:, subpath: nil, query: nil, content_type: nil, body: nil, headers: nil, save_to: nil, exception: true, ret: :data ) subpath = subpath.to_s if subpath.is_a?(Symbol) subpath = '' if subpath.nil? # File path (String or Pathname) or stream (responds to `write`) # Pathname also responds to `write` (overwrites file), so it must not be taken as a stream save_to = save_to.to_s if save_to.is_a?(Pathname) Aspera.assert(save_to.nil? || save_to.is_a?(String) || save_to.respond_to?(:write)) { "save_to: unsupported type #{save_to.class}" } Log.log.debug { "call #{operation} [#{subpath}]".red.bold.bg(:green) } Log.dump(:body, body, level: :trace1) Log.dump(:query, query, level: :trace1) Log.dump(:headers, headers, level: :trace1) Aspera.assert_type(subpath, String) # We must have a way to check return code Aspera.assert(exception || !ret.eql?(:data), 'ret: :data requires exception handler') req_headers, req_query = prepare_call(headers, query) result_http = nil result_data = nil # number of tries on error (first call included) error_tries = 1 + Parameters.instance.retry_max # OAuth token is renewed only once, independently of error retries token_renewed = false # start a block to be able to retry the actual HTTP request in case of OAuth token expiration begin Log.log.debug("send request (redirects=#{@redirect_max})") req = build_request(operation, subpath, req_query, content_type, body, req_headers) result_mime = nil file_saved = false # make http request (pipelined) http_session.request(req) do |response| result_http = response result_mime = Rest.parse_header(result_http['Content-Type'] || Mime::TEXT)[:type] Log.log.debug { "response: code=#{result_http.code}, mime=#{result_mime}, content-type=#{response['Content-Type']}" } # JSON data needs to be parsed, in case it contains an error code file_saved = save_response(response, result_mime, save_to) end Log.log.debug { "result: code=#{result_http.code} mime=#{result_mime}" } # sometimes there is a UTF8 char (e.g. (c) ) # TODO : related to mime type encoding ? # result_http.body.force_encoding('UTF-8') if result_http.body.is_a?(String) # Log.log.debug{"result: body=#{result_http.body}"} result_data = parse_response(result_http, result_mime) ErrorAnalyzer.instance.raise_on_error(req, result_data, result_http) unless file_saved || save_to.nil? raise 'save_to: IO object requires a streaming response' if save_to.respond_to?(:write) FileUtils.mkdir_p(File.dirname(save_to)) File.write(save_to, result_http.body, binmode: true) end rescue *NETWORK_ERRORS => e raise unless retry_error?(e) && (error_tries -= 1).positive? Log.log.warn { "#{e.class}: #{e.}: retrying" } retry_sleep retry rescue CallError => e # not authorized: OAuth token expired if !token_renewed && @not_auth_codes.include?(result_http.code.to_s) && @auth_params[:type].eql?(:oauth2) token_renewed = true = unless .nil? Log.log.debug('using new token') req_headers['Authorization'] = retry end end if retry_error?(e) && (error_tries -= 1).positive? retry_sleep retry end # redirect ? (any code beginning with 3) if e.response.is_a?(Net::HTTPRedirection) && @redirect_max.positive? return redirect_call( req.uri, e.response['Location'], operation: operation, body: body, content_type: content_type, save_to: save_to, exception: exception, headers: headers, ret: ret ) end # raise exception if could not retry and not return error in result raise e if exception end Log.log.debug { "result=http:#{result_http}, data:#{result_data.class}" } return case ret when :data then result_data when :resp then result_http when :both then [result_data, result_http] else Aspera.error_unexpected_value(ret) { 'Type of result for REST' } end end |
#cancel(subpath, **kwargs) ⇒ Object
CANCEL
485 |
# File 'lib/aspera/rest/client.rb', line 485 def cancel(subpath, **kwargs) = call(operation: 'CANCEL', subpath: subpath, **json_call_args(kwargs)) |
#create(subpath, params, **kwargs) ⇒ Object
POST JSON body
458 |
# File 'lib/aspera/rest/client.rb', line 458 def create(subpath, params, **kwargs) = call(operation: 'POST', subpath: subpath, body: params, **json_call_args(kwargs, body: true)) |
#delete(subpath, params = nil, **kwargs) ⇒ Object
DELETE
479 |
# File 'lib/aspera/rest/client.rb', line 479 def delete(subpath, params = nil, **kwargs) = call(operation: 'DELETE', subpath: subpath, query: params, **json_call_args(kwargs)) |
#oauth ⇒ OAuth::Base
OAuth object used for authorization, when auth type is :oauth2
114 115 116 117 118 119 120 121 122 |
# File 'lib/aspera/rest/client.rb', line 114 def oauth if @oauth.nil? Aspera.assert(@auth_params[:type].eql?(:oauth2), 'no OAuth defined') oauth_parameters = @auth_params.reject { |k, _v| k.eql?(:type) } Log.dump(:oauth_parameters, oauth_parameters) @oauth = OAuth::Factory.instance.create(**oauth_parameters) end return @oauth end |
#params ⇒ Hash
Parameters to create a copy of this object, e.g. Rest::Client.new(**api.params)
58 59 60 61 62 63 64 65 66 |
# File 'lib/aspera/rest/client.rb', line 58 def params return { base_url: @base_url, # String auth: @auth_params.dup, # Hash not_auth_codes: @not_auth_codes.dup, # Array redirect_max: @redirect_max, # Integer headers: @headers.dup # Hash } end |
#read(subpath, query = nil, **kwargs) ⇒ Object
GET
465 |
# File 'lib/aspera/rest/client.rb', line 465 def read(subpath, query = nil, **kwargs) = call(operation: 'GET', subpath: subpath, query: query, **json_call_args(kwargs)) |
#update(subpath, params, **kwargs) ⇒ Object
PUT JSON body
472 |
# File 'lib/aspera/rest/client.rb', line 472 def update(subpath, params, **kwargs) = call(operation: 'PUT', subpath: subpath, body: params, **json_call_args(kwargs, body: true)) |