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.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.travis.yml +5 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +21 -0
- data/README.md +41 -0
- data/Rakefile +10 -0
- data/bin/console +17 -0
- data/bin/setup +8 -0
- data/exe/ubiquity-iconik +3 -0
- data/lib/ubiquity/iconik.rb +7 -0
- data/lib/ubiquity/iconik/api/client.rb +708 -0
- data/lib/ubiquity/iconik/api/client/http_client.rb +333 -0
- data/lib/ubiquity/iconik/api/client/paginator.rb +134 -0
- data/lib/ubiquity/iconik/api/client/requests.rb +2 -0
- data/lib/ubiquity/iconik/api/client/requests/base_request.rb +270 -0
- data/lib/ubiquity/iconik/api/utilities.rb +174 -0
- data/lib/ubiquity/iconik/version.rb +5 -0
- data/ubiquity-iconik.gemspec +27 -0
- metadata +105 -0
@@ -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
|