ubiquity-wiredrive 1.0.0

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