ubiquity-iconik 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|