lib/dkron-rb/api_client.rb in dkron-rb-0.9.2 vs lib/dkron-rb/api_client.rb in dkron-rb-0.10.0

- old
+ new

@@ -1,26 +1,15 @@ =begin #Dkron REST API #You can communicate with Dkron using a RESTful JSON API over HTTP. Dkron nodes usually listen on port `8080` for API requests. All examples in this section assume that you've found a running leader at `localhost:8080`. Dkron implements a RESTful JSON API over HTTP to communicate with software clients. Dkron listens in port `8080` by default. All examples in this section assume that you're using the default port. Default API responses are unformatted JSON add the `pretty=true` param to format the response. -OpenAPI spec version: 0.9.2 +OpenAPI spec version: 0.10.0 Generated by: https://github.com/swagger-api/swagger-codegen.git +Swagger Codegen version: 2.3.1 -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - =end require 'date' require 'json' require 'logger' @@ -64,14 +53,22 @@ if @config.debugging @config.logger.debug "HTTP response body ~BEGIN~\n#{response.body}\n~END~\n" end unless response.success? - fail ApiError.new(:code => response.code, - :response_headers => response.headers, - :response_body => response.body), - response.status_message + if response.timed_out? + fail ApiError.new('Connection timed out') + elsif response.code == 0 + # Errors from libcurl will be made visible here + fail ApiError.new(:code => 0, + :message => response.return_message) + else + fail ApiError.new(:code => response.code, + :response_headers => response.headers, + :response_body => response.body), + response.status_message + end end if opts[:return_type] data = deserialize(response, opts[:return_type]) else @@ -123,38 +120,43 @@ if @config.debugging @config.logger.debug "HTTP request body param ~BEGIN~\n#{req_body}\n~END~\n" end end - Typhoeus::Request.new(url, req_opts) + request = Typhoeus::Request.new(url, req_opts) + download_file(request) if opts[:return_type] == 'File' + request end # Check if the given MIME is a JSON MIME. # JSON MIME examples: # application/json # application/json; charset=UTF8 # APPLICATION/JSON + # */* # @param [String] mime MIME - # @return [Boolean] True if the MIME is applicaton/json + # @return [Boolean] True if the MIME is application/json def json_mime?(mime) - !(mime =~ /\Aapplication\/json(;.*)?\z/i).nil? + (mime == "*/*") || !(mime =~ /Application\/.*json(?!p)(;.*)?/i).nil? end # Deserialize the response to the given return type. # # @param [Response] response HTTP response # @param [String] return_type some examples: "User", "Array[User]", "Hash[String,Integer]" def deserialize(response, return_type) body = response.body + + # handle file downloading - return the File instance processed in request callbacks + # note that response body is empty when the file is written in chunks in request on_body callback + return @tempfile if return_type == 'File' + return nil if body.nil? || body.empty? # return response body directly for String return type return body if return_type == 'String' - # handle file downloading - save response body into a tmp file and return the File instance - return download_file(response) if return_type == 'File' - # ensuring a default content type content_type = response.headers['Content-Type'] || 'application/json' fail "Content-Type is not supported: #{content_type}" unless json_mime?(content_type) @@ -213,34 +215,42 @@ end end # Save response body into a file in (the defined) temporary folder, using the filename # from the "Content-Disposition" header if provided, otherwise a random filename. + # The response body is written to the file in chunks in order to handle files which + # size is larger than maximum Ruby String or even larger than the maximum memory a Ruby + # process can use. # # @see Configuration#temp_folder_path - # @return [Tempfile] the file downloaded - def download_file(response) - content_disposition = response.headers['Content-Disposition'] - if content_disposition and content_disposition =~ /filename=/i - filename = content_disposition[/filename=['"]?([^'"\s]+)['"]?/, 1] - prefix = sanitize_filename(filename) - else - prefix = 'download-' - end - prefix = prefix + '-' unless prefix.end_with?('-') - + def download_file(request) tempfile = nil - encoding = response.body.encoding - Tempfile.open(prefix, @config.temp_folder_path, encoding: encoding) do |file| - file.write(response.body) - tempfile = file + encoding = nil + request.on_headers do |response| + content_disposition = response.headers['Content-Disposition'] + if content_disposition and content_disposition =~ /filename=/i + filename = content_disposition[/filename=['"]?([^'"\s]+)['"]?/, 1] + prefix = sanitize_filename(filename) + else + prefix = 'download-' + end + prefix = prefix + '-' unless prefix.end_with?('-') + encoding = response.body.encoding + tempfile = Tempfile.open(prefix, @config.temp_folder_path, encoding: encoding) + @tempfile = tempfile end - @config.logger.info "Temp file written to #{tempfile.path}, please copy the file to a proper folder "\ - "with e.g. `FileUtils.cp(tempfile.path, '/new/file/path')` otherwise the temp file "\ - "will be deleted automatically with GC. It's also recommended to delete the temp file "\ - "explicitly with `tempfile.delete`" - tempfile + request.on_body do |chunk| + chunk.force_encoding(encoding) + tempfile.write(chunk) + end + request.on_complete do |response| + tempfile.close + @config.logger.info "Temp file written to #{tempfile.path}, please copy the file to a proper folder "\ + "with e.g. `FileUtils.cp(tempfile.path, '/new/file/path')` otherwise the temp file "\ + "will be deleted automatically with GC. It's also recommended to delete the temp file "\ + "explicitly with `tempfile.delete`" + end end # Sanitize filename by removing path. # e.g. ../../sun.gif becomes sun.gif # @@ -267,11 +277,11 @@ if header_params['Content-Type'] == 'application/x-www-form-urlencoded' || header_params['Content-Type'] == 'multipart/form-data' data = {} form_params.each do |key, value| case value - when File, Array, nil + when ::File, ::Array, nil # let typhoeus handle File, Array and nil parameters data[key] = value else data[key] = value.to_s end @@ -285,10 +295,10 @@ end # Update hearder and query params based on authentication settings. # # @param [Hash] header_params Header parameters - # @param [Hash] form_params Query parameters + # @param [Hash] query_params Query parameters # @param [String] auth_names Authentication scheme name def update_params_for_auth!(header_params, query_params, auth_names) Array(auth_names).each do |auth_name| auth_setting = @config.auth_settings[auth_name] next unless auth_setting