ubiquity-iconik 1.1.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,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