ubiquity-wiredrive 1.0.0

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.
@@ -0,0 +1,3 @@
1
+ # module Ubiquity::Wiredrive::API::V3::Client::Requests end
2
+
3
+ require 'ubiquity/wiredrive/api/v3/client/requests/base_request'
@@ -0,0 +1,238 @@
1
+ require 'cgi'
2
+
3
+ module Ubiquity
4
+ module Wiredrive
5
+ module API
6
+ module V3
7
+ class Client
8
+ class Requests
9
+ class BaseRequest
10
+
11
+ HTTP_METHOD = :get
12
+ HTTP_BASE_PATH = ''
13
+ HTTP_PATH = ''
14
+ HTTP_SUCCESS_CODE = 200
15
+
16
+ DEFAULT_PARAMETER_SEND_IN_VALUE = :query
17
+
18
+ PARAMETERS = []
19
+
20
+ attr_accessor :client, :arguments, :options, :initial_arguments, :initial_options, :missing_required_arguments,
21
+ :default_parameter_send_in_value, :processed_parameters, :initialized, :response
22
+
23
+ attr_writer :parameters, :path, :body, :query
24
+
25
+ def self.normalize_argument_hash_keys(hash)
26
+ return hash unless hash.is_a?(Hash)
27
+ Hash[hash.dup.map { |k, v| [normalize_parameter_name(k), v] }]
28
+ end
29
+
30
+ def self.normalize_parameter_name(name)
31
+ (name || '').respond_to?(:to_s) ? name.to_s.gsub('_', '').gsub('-', '').downcase : name
32
+ end
33
+
34
+ def self.process_parameter(param, args = {}, args_out = {}, missing_required_arguments = [], processed_parameters = {}, default_parameter_send_in_value = DEFAULT_PARAMETER_SEND_IN_VALUE, options = {})
35
+ args = normalize_argument_hash_keys(args) || {} if options.fetch(:normalize_argument_hash_keys, false)
36
+
37
+ _k = param.is_a?(Hash) ? param : { :name => param, :required => false, :send_in => default_parameter_send_in_value }
38
+ _k[:send_in] ||= default_parameter_send_in_value
39
+
40
+ proper_parameter_name = _k[:name]
41
+ param_name = normalize_parameter_name(proper_parameter_name)
42
+ arg_key = (has_key = args.has_key?(param_name)) ?
43
+ param_name :
44
+ ((_k[:aliases] || []).map { |a| normalize_parameter_name(a) }.find { |a| has_key = args.has_key?(a) } || param_name)
45
+
46
+ value = has_key ? args[arg_key] : _k[:default_value]
47
+ is_set = has_key || _k.has_key?(:default_value)
48
+
49
+ processed_parameters[proper_parameter_name] = _k.merge(:value => value, :is_set => is_set)
50
+
51
+ unless is_set
52
+ missing_required_arguments << proper_parameter_name if _k[:required]
53
+ else
54
+ args_out[proper_parameter_name] = value
55
+ end
56
+
57
+ { :arguments_out => args_out, :processed_parameters => processed_parameters, :missing_required_arguments => missing_required_arguments }
58
+ rescue => e
59
+ raise e, "Error Processing Parameter: #{param.inspect} Args: #{args.inspect}. #{e.message}"
60
+ end
61
+
62
+ def self.process_parameters(params, args, options = {})
63
+ args = normalize_argument_hash_keys(args) || {}
64
+ args_out = options[:arguments_out] || {}
65
+ default_parameter_send_in_value = options[:default_parameter_send_in_value] || DEFAULT_PARAMETER_SEND_IN_VALUE
66
+ processed_parameters = options[:processed_parameters] || {}
67
+ missing_required_arguments = options[:missing_required_arguments] || []
68
+
69
+ params.each do |param|
70
+ process_parameter(param, args, args_out, missing_required_arguments, processed_parameters, default_parameter_send_in_value)
71
+ end
72
+ { :arguments_out => args_out, :processed_parameters => processed_parameters, :missing_required_arguments => missing_required_arguments }
73
+ end
74
+
75
+ def initialize(args = {}, options = {})
76
+ @initial_arguments = args.dup
77
+ @initial_options = options.dup
78
+
79
+ @options = options.dup
80
+
81
+ initialize_attributes if options.fetch(:initialize_attributes, true)
82
+ after_initialize
83
+ end
84
+
85
+ def after_initialize
86
+ process_parameters if initialized
87
+ end
88
+
89
+ def initialize_attributes
90
+ @client = options[:client]
91
+ @missing_required_arguments = []
92
+ @default_parameter_send_in_value = options[:default_parameter_send_in_value] || self.class::DEFAULT_PARAMETER_SEND_IN_VALUE
93
+ @processed_parameters = {}
94
+ @arguments = {}
95
+ @eval_http_path = options.fetch(:eval_http_path, true)
96
+ @base_path = options[:base_path]
97
+
98
+ @parameters = options[:parameters]
99
+ @http_method = options[:http_method]
100
+ @http_path = options[:http_path] ||= options[:path_raw]
101
+
102
+ @path = options[:path]
103
+ @path_arguments = nil
104
+ @path_only = nil
105
+
106
+ @query = options[:query]
107
+ @query_arguments = nil
108
+
109
+ @body = options[:body]
110
+ @body_arguments = nil
111
+
112
+ @response = nil
113
+
114
+ @http_success_code = options[:http_success_code] || HTTP_SUCCESS_CODE
115
+
116
+ @initialized = true
117
+ end
118
+
119
+ alias :reset_attributes :initialize_attributes
120
+
121
+ def process_parameters(params = parameters, args = @initial_arguments, _options = @options)
122
+ before_process_parameters unless _options.fetch(:skip_before_process_parameters, false)
123
+ self.class.process_parameters(params, args, _options.merge(:processed_parameters => processed_parameters, :missing_required_arguments => missing_required_arguments, :default_parameter_send_in_value => default_parameter_send_in_value, :arguments_out => arguments))
124
+ after_process_parameters unless _options.fetch(:skip_after_process_parameters, false)
125
+ end
126
+
127
+ def before_process_parameters
128
+ # TO BE IMPLEMENTED IN CHILD CLASS
129
+ end
130
+
131
+ def after_process_parameters
132
+ # TO BE IMPLEMENTED IN CHILD CLASS
133
+ end
134
+
135
+ alias :post_process_arguments :after_process_parameters
136
+
137
+ # @!group Attribute Readers
138
+
139
+ def http_success_code
140
+ @http_success_code
141
+ end
142
+
143
+ def arguments
144
+ @arguments ||= {}
145
+ end
146
+
147
+ def base_path
148
+ @base_path ||= self.class::HTTP_BASE_PATH
149
+ end
150
+
151
+ def body_arguments
152
+ @body_arguments ||= arguments.dup.delete_if { |k, _| processed_parameters[k][:send_in] != :body }
153
+ end
154
+
155
+ def body
156
+ @body ||= body_arguments.empty? ? nil : body_arguments
157
+ end
158
+
159
+ def client
160
+ @client ||= options[:client]
161
+ end
162
+
163
+ def eval_http_path?
164
+ @eval_http_path
165
+ end
166
+
167
+ def http_path
168
+ @http_path ||= self.class::HTTP_PATH
169
+ end
170
+
171
+ def http_method
172
+ @http_method ||= self.class::HTTP_METHOD
173
+ end
174
+
175
+ def parameters
176
+ @parameters ||= self.class::PARAMETERS.dup
177
+ end
178
+
179
+ def relative_path
180
+ @relative_path ||= (path.start_with?('/') ? path[1..-1] : path)
181
+ end
182
+
183
+ # The URI Path
184
+ def path
185
+ @path ||= File.join(base_path, (eval_http_path? ? eval(%("#{http_path}"), binding, __FILE__, __LINE__) : http_path))
186
+ end
187
+
188
+ def path_arguments
189
+ @path_arguments ||= Hash[
190
+ arguments.dup.delete_if { |k, _| processed_parameters[k][:send_in] != :path }.
191
+ map { |k, v| [k, CGI.escape(v.respond_to?(:to_s) ? v.to_s : '').gsub('+', '%20')] }
192
+ ]
193
+ end
194
+
195
+ def query
196
+ @query ||= begin
197
+ query_arguments.is_a?(Hash) ? query_arguments.map { |k, v| "#{CGI.escape(k.to_s).gsub('+', '%20')}=#{CGI.escape([*v].join(',')).gsub('+', '%20')}" }.join('&') : query_arguments
198
+ end
199
+ end
200
+
201
+ def query_arguments
202
+ @query_arguments ||= arguments.dup.delete_if { |k, _| processed_parameters[k][:send_in] != :query }
203
+ end
204
+
205
+ def uri_request_path
206
+ [path].concat([*query].delete_if { |v| v.respond_to?(:empty?) and v.empty? }).join('?')
207
+ end
208
+
209
+ # @!endgroup
210
+
211
+ # def response
212
+ # client.response if client
213
+ # end
214
+
215
+ def http_client
216
+ client.http_client
217
+ end
218
+
219
+ def http_response
220
+ @http_response ||= http_client.response.dup rescue nil
221
+ end
222
+
223
+ def execute
224
+ @response = http_client.call_method(http_method, { :path => relative_path, :query => query, :body => body }, options) if client
225
+ end
226
+
227
+ def success?
228
+ _response = http_response and ([*http_success_code].include?(http_response.code))
229
+ _response
230
+ end
231
+
232
+ end
233
+ end
234
+ end
235
+ end
236
+ end
237
+ end
238
+ end
@@ -0,0 +1,256 @@
1
+ require 'cgi'
2
+ require 'json'
3
+ require 'logger'
4
+ require 'net/http'
5
+ require 'net/https'
6
+ require 'net/http/post/multipart'
7
+
8
+ require 'ubiquity/wiredrive/version'
9
+
10
+ module Ubiquity
11
+ module Wiredrive
12
+ module API
13
+ module V3
14
+ class HTTPClient
15
+
16
+ class RateLimitException < StandardError; end
17
+
18
+ # Ruby uses all lower case headers but Jetty uses case sensitive headers
19
+ class CaseSensitiveHeaderKey < String
20
+ def downcase; self end
21
+ def capitalize; self end
22
+ end
23
+
24
+
25
+ attr_accessor :logger, :http, :http_host_address, :http_host_port, :base_uri
26
+ attr_accessor :hostname, :username, :password
27
+
28
+ attr_accessor :default_request_headers, :authorization_header_name, :authorization_header_value
29
+
30
+ attr_accessor :log_request_body, :log_response_body, :log_pretty_print_body
31
+
32
+ attr_accessor :request, :response
33
+
34
+ DEFAULT_HTTP_HOST_ADDRESS = 'api.wiredrive.com'
35
+ DEFAULT_HTTP_HOST_PORT = 443
36
+
37
+ def initialize(args = { })
38
+ args = args.dup
39
+ initialize_logger(args)
40
+
41
+ @http_host_address = args[:http_host_address] ||= DEFAULT_HTTP_HOST_ADDRESS
42
+ @http_host_port = args[:http_host_port] ||= DEFAULT_HTTP_HOST_PORT
43
+
44
+ initialize_http(args)
45
+
46
+ @user_agent_default = "Ubiquity Wiredrive Ruby SDK Version #{Ubiquity::Wiredrive::VERSION}"
47
+
48
+ @authorization_header_name = 'Authorization' # CaseSensitiveHeaderKey.new('Authorization')
49
+ @authorization_header_value = nil # @auth_token # "Bearer #{@auth_token}"
50
+
51
+ @default_request_headers = {
52
+ 'user-agent' => @user_agent_default,
53
+ 'Content-Type' => 'application/json; charset=utf-8',
54
+ 'Accept' => 'application/json',
55
+ }
56
+
57
+ _auth_token = args[:auth_token]
58
+ self.auth_token = _auth_token if _auth_token
59
+
60
+ @log_request_body = args.fetch(:log_request_body, true)
61
+ @log_response_body = args.fetch(:log_response_body, true)
62
+ @log_pretty_print_body = args.fetch(:log_pretty_print_body, true)
63
+
64
+ @cancelled = false
65
+ @delay_between_rate_limit_retries = 5
66
+ @parse_response = args.fetch(:parse_response, true)
67
+ end
68
+
69
+ def http_host_address=(value)
70
+ @http_host_address = value
71
+ initialize_http
72
+ end
73
+
74
+ def initialize_logger(args = { })
75
+ @logger = args[:logger] ||= Logger.new(args[:log_to] || STDOUT)
76
+ log_level = args[:log_level]
77
+ if log_level
78
+ @logger.level = log_level
79
+ args[:logger] = @logger
80
+ end
81
+ @logger
82
+ end
83
+
84
+ def initialize_http(args = { })
85
+ @http = Net::HTTP.new(http_host_address, http_host_port)
86
+ http.use_ssl = true
87
+
88
+ # TODO Add SSL Patch
89
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
90
+
91
+ @base_uri = args[:base_uri] || "http#{http.use_ssl? ? 's' : ''}://#{http.address}:#{http.port}/"
92
+
93
+ http
94
+ end
95
+
96
+ # Formats a HTTPRequest or HTTPResponse body for log output.
97
+ # @param [HTTPRequest|HTTPResponse] obj
98
+ # @return [String]
99
+ def format_body_for_log_output(obj)
100
+ case obj.content_type
101
+ when 'application/json'
102
+ return obj.body unless @log_pretty_print_body
103
+ _body = obj.body
104
+ JSON.pretty_generate(JSON.parse(_body)) rescue _body
105
+ when 'application/xml'; obj.body
106
+ else obj.body.inspect
107
+ end
108
+ end
109
+
110
+ # @param [Net::HTTPRequest] request
111
+ def send_request(request)
112
+ @response_parsed = nil
113
+ @request = request
114
+
115
+ begin
116
+ logger.debug { %(REQUEST: #{request.method} http#{http.use_ssl? ? 's' : ''}://#{http.address}:#{http.port}#{request.path} HEADERS: #{request.to_hash.inspect} #{log_request_body and request.request_body_permitted? ? "\n-- BODY BEGIN --\n#{format_body_for_log_output(request)}\n-- BODY END --" : ''}) }
117
+
118
+ @response = http.request(request)
119
+ logger.debug { %(RESPONSE: #{response.inspect} HEADERS: #{response.to_hash.inspect} #{log_response_body and response.respond_to?(:body) ? "\n-- BODY BEGIN --\n#{format_body_for_log_output(response)}\n-- BODY END--" : ''}) }
120
+ raise RateLimitException, "#{response.to_hash.inspect}" if response.code == '420'
121
+ rescue RateLimitException => e
122
+ logger.warn { "Rate Limited. Will retry in #{@delay_between_rate_limit_retries} seconds." }
123
+ sleep_break @delay_between_rate_limit_retries
124
+ retry unless @cancelled
125
+ end
126
+
127
+ @parse_response ? response_parsed : response.body
128
+ end
129
+
130
+ def sleep_break(seconds)
131
+ while (seconds > 0)
132
+ sleep(1)
133
+ seconds -= 1
134
+ break if @cancelled
135
+ end
136
+ end
137
+
138
+ def response_parsed
139
+ @response_parsed ||= begin
140
+ response_content_type = response.content_type
141
+ logger.debug { "Parsing Response: #{response_content_type}" }
142
+
143
+ case response_content_type
144
+ when 'application/json'
145
+ JSON.parse(response.body) rescue response
146
+ # when 'text/html'
147
+ # when 'text/plain'
148
+ else
149
+ response.body
150
+ end
151
+ end
152
+ end
153
+
154
+ def build_uri(path = '', query = nil)
155
+ _query = query.is_a?(Hash) ? query.map { |k,v| "#{CGI.escape(k.to_s)}=#{CGI.escape(v)}" }.join('&') : query
156
+ _path = "#{path}#{_query and _query.respond_to?(:empty?) and !_query.empty? ? "?#{_query}" : ''}"
157
+ URI.parse(File.join(base_uri, _path))
158
+ end
159
+
160
+ if RUBY_VERSION.start_with? '1.8.'
161
+ def request_method_name_to_class_name(method_name)
162
+ method_name.to_s.capitalize
163
+ end
164
+ else
165
+ def request_method_name_to_class_name(method_name)
166
+ method_name.to_s.capitalize.to_sym
167
+ end
168
+ end
169
+
170
+ def auth_token=(value)
171
+ @auth_token = value
172
+ default_request_headers[authorization_header_name] = @auth_token # "Bearer #{@auth_token}"
173
+ end
174
+
175
+ # @param [Symbol] method_name (:get)
176
+ # @param [Hash] args
177
+ # @option args [Hash] :headers ({})
178
+ # @option args [String] :path ('')
179
+ # @option args [Hash] :query ({})
180
+ # @option args [Any] :body (nil)
181
+ # @param [Hash] options
182
+ # @option options [Hash] :default_request_headers (@default_request_headers)
183
+ def call_method(method_name = :get, args = { }, options = { })
184
+ headers = args[:headers] || options[:headers] || { }
185
+ path = args[:path] || ''
186
+ query = args[:query] || { }
187
+ body = args[:body]
188
+
189
+ # Allow the default request headers to be overridden
190
+ _default_request_headers = options.fetch(:default_request_headers, default_request_headers)
191
+ _default_request_headers ||= { }
192
+ _headers = _default_request_headers.merge(headers)
193
+
194
+ @uri = build_uri(path, query)
195
+ klass_name = request_method_name_to_class_name(method_name)
196
+ klass = Net::HTTP.const_get(klass_name)
197
+
198
+ request = klass.new(@uri.request_uri, _headers)
199
+
200
+ if request.request_body_permitted?
201
+ _body = (body and !body.is_a?(String)) ? JSON.generate(body) : body
202
+ logger.debug { "Processing Body: '#{_body}'" }
203
+ request.body = _body if _body
204
+ end
205
+
206
+ send_request(request)
207
+ end
208
+
209
+ def delete(path, options = { })
210
+ query = options.fetch(:query, { })
211
+ @uri = build_uri(path, query)
212
+
213
+
214
+ request = Net::HTTP::Delete.new(@uri.request_uri, default_request_headers)
215
+ body = options[:body]
216
+ if body
217
+ body = JSON.generate(body) unless body.is_a?(String)
218
+ request.body = body
219
+ end
220
+
221
+ send_request(request)
222
+ end
223
+
224
+ def get(path, query = nil, options = { })
225
+ query ||= options.fetch(:query, { })
226
+ @uri = build_uri(path, query)
227
+ request = Net::HTTP::Get.new(@uri.request_uri, default_request_headers)
228
+ send_request(request)
229
+ end
230
+
231
+ def put(path, body, options = { })
232
+ query = options.fetch(:query, { })
233
+ @uri = build_uri(path, query)
234
+ body = JSON.generate(body) unless body.is_a?(String)
235
+
236
+ request = Net::HTTP::Put.new(@uri.request_uri, default_request_headers)
237
+ request.body = body
238
+ send_request(request)
239
+ end
240
+
241
+ def post(path, body, options = { })
242
+ query = options.fetch(:query, { })
243
+ @uri = build_uri(path, query)
244
+ body = JSON.generate(body) unless body.is_a?(String)
245
+
246
+ request = Net::HTTP::Post.new(@uri.request_uri, default_request_headers)
247
+ request.body = body
248
+ send_request(request)
249
+ end
250
+
251
+ end
252
+
253
+ end
254
+ end
255
+ end
256
+ end