ubiquity-iconik 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,333 @@
1
+ require 'json'
2
+ require 'net/https'
3
+
4
+ if RUBY_VERSION.start_with?('1.8')
5
+ class Net::HTTP::Patch < Net::HTTPRequest
6
+ METHOD = 'PATCH'
7
+ REQUEST_HAS_BODY = true
8
+ RESPONSE_HAS_BODY = true
9
+ end
10
+ end
11
+
12
+ module Ubiquity
13
+ module Iconik
14
+ module API
15
+ class Client
16
+
17
+ class HTTPClient
18
+
19
+ class HTTPAuthorizationError < RuntimeError; end
20
+
21
+ attr_accessor :logger, :http, :http_host_address, :http_host_port, :base_uri
22
+ attr_accessor :username, :password
23
+
24
+ attr_accessor :default_request_headers,
25
+ :header_auth_key
26
+
27
+ attr_accessor :log_request_body, :log_response_body, :log_pretty_print_body
28
+
29
+ attr_accessor :request, :response, :use_exceptions
30
+
31
+
32
+ attr_accessor :app_id, :token, :token_data
33
+
34
+ DEFAULT_HTTP_HOST_ADDRESS = 'app.iconik.io'
35
+ DEFAULT_HTTP_HOST_PORT = 443
36
+
37
+ DEFAULT_BASE_PATH = '/API/'
38
+
39
+ DEFAULT_APP_ID = ''
40
+
41
+ DEFAULT_HEADER_CONTENT_TYPE = 'application/json; charset=utf-8'
42
+ DEFAULT_HEADER_ACCEPTS = 'application/json'
43
+
44
+ DEFAULT_HEADER_AUTH_KEY = 'Auth-Token'
45
+
46
+ def initialize(args = { })
47
+ args = args.dup
48
+
49
+ @use_exceptions = args.fetch(:use_exceptions, true)
50
+ @max_retry_count = 1
51
+ @retry_count = 0
52
+
53
+ initialize_logger(args)
54
+ initialize_http(args)
55
+
56
+ logger.debug { "#{self.class.name}::#{__method__} Arguments: #{args.inspect}" }
57
+
58
+ @base_uri = args[:base_uri] || "http#{http.use_ssl? ? 's' : ''}://#{http.address}:#{http.port}"
59
+ @default_base_path = args[:default_base_path] || DEFAULT_BASE_PATH
60
+
61
+ # @user_agent_default = "Ubiquity::Iconik Ruby SDK Version #{Ubiquity::Iconik::VERSION}"
62
+
63
+ @app_id = args[:application_id] || args[:app_id] || DEFAULT_APP_ID
64
+
65
+ content_type = args[:content_type_header] ||= DEFAULT_HEADER_CONTENT_TYPE
66
+ accepts = args[:accepts_header] ||= args[:accept_header] || DEFAULT_HEADER_ACCEPTS
67
+
68
+ @header_auth_key = args[:header_auth_key] || DEFAULT_HEADER_AUTH_KEY
69
+
70
+ @default_request_headers = {
71
+ # 'User-Agent' => @user_agent_default,
72
+ 'Content-Type' => content_type,
73
+ 'Accept' => accepts,
74
+ 'App-ID' => @app_id,
75
+ }
76
+
77
+ self.token = args[:token] || ''
78
+
79
+ @log_request_body = args.fetch(:log_request_body, true)
80
+ @log_response_body = args.fetch(:log_response_body, true)
81
+ @log_pretty_print_body = args.fetch(:log_pretty_print_body, true)
82
+
83
+ @parse_response = args.fetch(:parse_response, true)
84
+ end
85
+
86
+ def initialize_logger(args = { })
87
+ @logger = args[:logger] ||= Logger.new(args[:log_to] || STDOUT)
88
+ log_level = args[:log_level]
89
+ if log_level
90
+ @logger.level = log_level
91
+ args[:logger] = @logger
92
+ end
93
+ @logger
94
+ end
95
+
96
+ def initialize_http(args = { })
97
+ @http_host_address = args[:http_host_address] ||= DEFAULT_HTTP_HOST_ADDRESS
98
+ @http_host_port = args[:http_host_port] ||= DEFAULT_HTTP_HOST_PORT
99
+ @http = Net::HTTP.new(http_host_address, http_host_port)
100
+
101
+ use_ssl = args.fetch(:http_host_use_ssl, true)
102
+ if use_ssl
103
+ @http.use_ssl = true
104
+ http_verify_mode = args[:http_host_ssl_verify_mode] #|| OpenSSL::SSL::VERIFY_NONE
105
+ @http.verify_mode = http_verify_mode if http_verify_mode
106
+ end
107
+
108
+ http
109
+ end
110
+
111
+ # Formats a HTTPRequest or HTTPResponse body for log output.
112
+ # @param [HTTPRequest|HTTPResponse] obj
113
+ # @return [String]
114
+ def format_body_for_log_output(obj)
115
+ if obj.content_type == 'application/json'
116
+ if @log_pretty_print_body
117
+ _body = obj.body
118
+ output = JSON.pretty_generate(JSON.parse(_body)) rescue _body
119
+ return output
120
+ else
121
+ return obj.body
122
+ end
123
+ elsif obj.content_type == 'application/xml'
124
+ return obj.body
125
+ else
126
+ return obj.body.inspect
127
+ end
128
+ end
129
+
130
+ # @param [HTTPRequest] request
131
+ def send_request(request)
132
+ @response_parsed = nil
133
+ @request = request
134
+ 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 --" : ''}) }
135
+
136
+ @response = http.request(request)
137
+ 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--" : ''}) }
138
+ #logger.debug { "Parse Response? #{@parse_response}" }
139
+
140
+ raise HTTPAuthorizationError if @use_exceptions && @response.code == '401'
141
+
142
+ @retry_count = 0
143
+ @original_exception = nil
144
+
145
+ @parse_response ? response_parsed : response.body
146
+
147
+ rescue => e
148
+ raise if @retry_count >= @max_retry_count
149
+
150
+ @retry_count += 1
151
+ logger.warn { "Retrying request. Attempt #{@retry_count} '#{e.message}'" }
152
+ retry
153
+ end
154
+
155
+ def response_parsed
156
+ @response_parsed ||= begin
157
+ response_body = response.respond_to?(:body) ? response.body : ''
158
+ logger.debug { "Parsing Response. #{response_body.inspect}" }
159
+
160
+ case response.content_type
161
+ when 'application/json'
162
+ response_body.empty? ? response_body : JSON.parse(response_body) # rescue response
163
+ else
164
+ response_body
165
+ end
166
+ end
167
+ end
168
+
169
+ # @param [String] path
170
+ # @param [Hash|String|Nil] query
171
+ # @return [URI]
172
+ def build_uri(path = '', query = nil)
173
+ _query = query.is_a?(Hash) ? query.map { |k,v| "#{CGI.escape(k.to_s)}=#{CGI.escape(v.respond_to?(:to_s) ? v.to_s : v)}" }.join('&') : query
174
+ _path = "#{path}#{_query and _query.respond_to?(:empty?) and !_query.empty? ? "?#{_query}" : ''}"
175
+ URI.parse(File.join(base_uri, _path))
176
+ end
177
+
178
+ if RUBY_VERSION.start_with? '1.8.'
179
+ def request_method_name_to_class_name(method_name)
180
+ method_name.to_s.capitalize
181
+ end
182
+ else
183
+ def request_method_name_to_class_name(method_name)
184
+ method_name.to_s.capitalize.to_sym
185
+ end
186
+ end
187
+
188
+ # @param [Symbol] method_name (:get)
189
+ # @param [Hash] args
190
+ # @option args [Hash] :headers ({})
191
+ # @option args [String] :path ('')
192
+ # @option args [Hash] :query ({})
193
+ # @option args [Any] :body (nil)
194
+ # @param [Hash] options
195
+ # @option options [Hash] :default_request_headers (@default_request_headers)
196
+ def call_method(method_name = :get, args = { }, options = { })
197
+ headers = args[:headers] || options[:headers] || { }
198
+ path = args[:path] || ''
199
+ query = args[:query] || { }
200
+ body = args[:body]
201
+
202
+ # Allow the default request headers to be overridden
203
+ _default_request_headers = options.fetch(:default_request_headers, default_request_headers)
204
+ _default_request_headers ||= { }
205
+ _headers = _default_request_headers.merge(headers)
206
+
207
+ @uri = build_uri(path, query)
208
+ klass_name = request_method_name_to_class_name(method_name)
209
+ klass = Net::HTTP.const_get(klass_name)
210
+
211
+ request = klass.new(@uri.request_uri, _headers)
212
+
213
+ if request.request_body_permitted?
214
+ _body = (body and !body.is_a?(String)) ? JSON.generate(body) : body
215
+ logger.debug { "Processing Body: '#{_body}'" }
216
+ request.body = _body if _body
217
+ end
218
+
219
+ send_request(request)
220
+ end
221
+
222
+ def delete(path, options = { })
223
+ query = options.fetch(:query, { })
224
+ base_path = options[:base_path] || ( path.start_with?(@default_base_path) ? '' : @default_base_path )
225
+ @uri = build_uri(File.join(base_path, path), query)
226
+ request = Net::HTTP::Delete.new(@uri.request_uri, default_request_headers)
227
+ send_request(request)
228
+ end
229
+
230
+ def get(path, options = { })
231
+ # Allow the default request headers to be overridden
232
+ headers = options[:headers] || { }
233
+ _default_request_headers = options.fetch(:default_request_headers, default_request_headers) || { }
234
+ _headers = _default_request_headers.merge(headers)
235
+
236
+ query ||= options.fetch(:query, { })
237
+ base_path = options[:base_path] || ( path.start_with?(@default_base_path) ? '' : @default_base_path )
238
+ @uri = build_uri(File.join(base_path, path), query)
239
+ request = Net::HTTP::Get.new(@uri.request_uri, _headers)
240
+ send_request(request)
241
+ end
242
+
243
+ def head(path, options = { })
244
+ # Allow the default request headers to be overridden
245
+ headers = options[:headers] || { }
246
+ _default_request_headers = options.fetch(:default_request_headers, default_request_headers) || { }
247
+ _headers = _default_request_headers.merge(headers)
248
+
249
+ query ||= options.fetch(:query, { })
250
+ base_path = options[:base_path] || ( path.start_with?(@default_base_path) ? '' : @default_base_path )
251
+ @uri = build_uri(File.join(base_path, path), query)
252
+
253
+ request = Net::HTTP::Head.new(@uri.request_uri, _headers)
254
+ send_request(request)
255
+ end
256
+
257
+ def options(path, options = { })
258
+ # Allow the default request headers to be overridden
259
+ headers = options[:headers] || { }
260
+ _default_request_headers = options.fetch(:default_request_headers, default_request_headers) || { }
261
+ _headers = _default_request_headers.merge(headers)
262
+
263
+ query ||= options.fetch(:query, { })
264
+ base_path = options[:base_path] || ( path.start_with?(@default_base_path) ? '' : @default_base_path )
265
+ @uri = build_uri(File.join(base_path, path), query)
266
+ request = Net::HTTP::Options.new(@uri.request_uri, _headers)
267
+ send_request(request)
268
+ end
269
+
270
+ def put(path, body, options = { })
271
+ # Allow the default request headers to be overridden
272
+ headers = options[:headers] || { }
273
+ _default_request_headers = options.fetch(:default_request_headers, default_request_headers) || { }
274
+ _headers = _default_request_headers.merge(headers)
275
+
276
+ query = options.fetch(:query, { })
277
+ base_path = options[:base_path] || ( path.start_with?(@default_base_path) ? '' : @default_base_path )
278
+ @uri = build_uri(File.join(base_path, path), query)
279
+ request = Net::HTTP::Put.new(@uri.request_uri, _headers)
280
+
281
+ body = JSON.generate(body) if body and !body.is_a?(String)
282
+
283
+ request.body = body if body
284
+ send_request(request)
285
+ end
286
+
287
+ def post(path, body, options = { })
288
+ # Allow the default request headers to be overridden
289
+ headers = options[:headers] || { }
290
+ _default_request_headers = options.fetch(:default_request_headers, default_request_headers) || { }
291
+ _headers = _default_request_headers.merge(headers)
292
+
293
+ query = options.fetch(:query, { })
294
+ base_path = options[:base_path] || ( path.start_with?(@default_base_path) ? '' : @default_base_path )
295
+ @uri = build_uri(File.join(base_path, path), query)
296
+
297
+ request = Net::HTTP::Post.new(@uri.request_uri, _headers)
298
+
299
+ body = JSON.generate(body) if body and !body.is_a?(String)
300
+
301
+ request.body = body if body
302
+ send_request(request)
303
+ end
304
+
305
+
306
+ def default_header_auth_set
307
+ @default_request_headers[header_auth_key] = @token.respond_to?(:to_s) ? @token.to_s : @token
308
+ end
309
+
310
+ # Determines if token is set
311
+ def token?
312
+ (@token.respond_to?(:empty?) && !@token.empty?)
313
+ end
314
+
315
+ # Token setter
316
+ def token=(value)
317
+ @token = value || ''
318
+ default_header_auth_set
319
+ end
320
+
321
+ # HTTPClient
322
+ end
323
+
324
+ # Client
325
+ end
326
+
327
+ # API
328
+ end
329
+
330
+ # Iconik
331
+ end
332
+ # Ubiquity
333
+ end
@@ -0,0 +1,134 @@
1
+ class Ubiquity::Iconik::API::Client::Paginator
2
+
3
+ attr_accessor :api_client, :logger
4
+
5
+ def initialize(api_client, options = { })
6
+ initialize_logger(options)
7
+ @api_client = api_client
8
+ end
9
+
10
+ def initialize_logger(args = {})
11
+ @logger = args[:logger] ||= Logger.new(args[:log_to] || STDOUT)
12
+ log_level = args[:log_level]
13
+ if log_level
14
+ _logger = @logger.dup
15
+ _logger.level = log_level
16
+ @logger = _logger
17
+ end
18
+ @logger
19
+ end
20
+
21
+ def current_page
22
+ @current_page ||= last_response['page']
23
+ end
24
+
25
+ def http_client
26
+ api_client.http_client
27
+ end
28
+
29
+ def include_remaining_pages
30
+ # response = api_client.response.dup
31
+ @first_page = current_page
32
+ _results = last_results
33
+ _results.concat(remaining_pages_get)
34
+ _per_page = _results.length
35
+ last_response.merge({ 'page' => @first_page, 'per_page' => _per_page, 'objects' => _results })
36
+ end
37
+
38
+ def last_response
39
+ @last_response ||= api_client.http_client.response_parsed
40
+ end
41
+
42
+ def last_results
43
+ @last_results ||= last_response['objects']
44
+ end
45
+
46
+ def next_page?
47
+ current_page < total_pages
48
+ end
49
+
50
+ def next_page_get(_next_page_number = nil)
51
+ _next_page_number ||= next_page_number
52
+ page_get(_next_page_number)
53
+ end
54
+
55
+ def next_page_number
56
+ @next_page_number ||= current_page + 1
57
+ end
58
+
59
+ def page_size
60
+ @page_size ||= last_response['per_page']
61
+ end
62
+
63
+ def page_get(page_number)
64
+ logger.debug { "Getting Page #{page_number} of #{total_pages}" }
65
+ new_request = request.class.new(request_args_out.merge(:page => page_number), request_options_out)
66
+ _response = new_request.execute
67
+ process_response
68
+ end
69
+
70
+ def pages_get(pages, options = { })
71
+ consolidate = options.fetch(:consolidate, true)
72
+ pages = pages.to_a if pages.respond_to?(:to_a)
73
+ pages_out = pages.map { |v| page_get(v) }
74
+ pages_out.flatten! if consolidate
75
+ pages_out
76
+ end
77
+
78
+ def paginated?
79
+ @paginated = !!total_pages
80
+ end
81
+
82
+ def process_request
83
+ @current_page = nil
84
+ @total_results = nil
85
+ @has_next_page = nil
86
+ end
87
+
88
+ def process_response
89
+ @current_page = nil
90
+ @last_response = nil
91
+ @next_page_number = nil
92
+ @page_size = nil
93
+ @total_pages = nil
94
+ @total_results = nil
95
+ @paginated = paginated?
96
+
97
+ next_page_number
98
+
99
+ @last_results = last_response['objects']
100
+ end
101
+
102
+ def request
103
+ api_client.request
104
+ end
105
+
106
+ def request_args_out
107
+ @request_args_out ||= request.initial_arguments.dup
108
+ end
109
+
110
+ def request_options_out
111
+ @request_options_out ||= { :client => api_client }.merge request.initial_options.dup
112
+ end
113
+
114
+ def total_pages
115
+ last_response['pages']
116
+ end
117
+
118
+ def total_results
119
+ @total_results ||= last_response['total']
120
+ end
121
+
122
+ def remaining_pages_get
123
+ return [ ] unless paginated? && next_page?
124
+ remaining_results = [ ]
125
+ loop do
126
+ response = next_page_get
127
+ remaining_results.concat(response)
128
+
129
+ break unless next_page?
130
+ end
131
+ remaining_results
132
+ end
133
+
134
+ end