npolar-api-client-ruby 0.2.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 +15 -0
- data/.gitignore +19 -0
- data/Gemfile +16 -0
- data/LICENSE +674 -0
- data/README.md +57 -0
- data/bin/npolar-api +13 -0
- data/lib/npolar/api/client.rb +19 -0
- data/lib/npolar/api/client/json_api_client.rb +567 -0
- data/lib/npolar/api/client/npolar_api_command.rb +298 -0
- data/npolar-api-client-ruby.gemspec +25 -0
- metadata +67 -0
data/README.md
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
npolar-api-client-ruby
|
2
|
+
======================
|
3
|
+
UNSTABLE Ruby client for https://api.npolar.no, based on [Typhoeus](https://github.com/typhoeus/typhoeus)
|
4
|
+
|
5
|
+
## Features
|
6
|
+
|
7
|
+
* Handles POST of large JSON Arrays
|
8
|
+
* Parallel requests
|
9
|
+
* Mimicks well-known curl commands
|
10
|
+
* Automatic authentication on write operations
|
11
|
+
* Automatic Content-Type, Accept, and other headers
|
12
|
+
|
13
|
+
## npolar-api (command-line tool)
|
14
|
+
```
|
15
|
+
npolar-api [options] [https://api.npolar.no]/endpoint
|
16
|
+
|
17
|
+
npolar-api /schema
|
18
|
+
npolar-api -XPOST /endpoint --data=/file.json
|
19
|
+
npolar-api -XPOST /endpoint --data='{"title":"Title"}'
|
20
|
+
npolar-api -XPUT --headers http://admin:password@localhost:5984/testdb
|
21
|
+
npolar-api -XPUT --headers http://admin:password@localhost:5984/testdb/test1
|
22
|
+
npolar-api -XDELETE /endpoint/id
|
23
|
+
|
24
|
+
npolar-api is built on top of Typhoeus/libcurl.
|
25
|
+
For more information and source: https://github.com/npolar/npolar-api-ruby-client
|
26
|
+
|
27
|
+
Options:
|
28
|
+
--auth Force authorization
|
29
|
+
-d, --data=data Data (request body) for POST and PUT
|
30
|
+
--debug Debug (alias for --level=debug
|
31
|
+
-l, --level=level Log level
|
32
|
+
-X, --method=method HTTP method, GET is default
|
33
|
+
-H, --header=header Add HTTP request header
|
34
|
+
--ids=ids URI that returns identifiers
|
35
|
+
--join Use --join with --ids to join documents into a JSON array
|
36
|
+
-c, --concurrency=number Concurrency (max)
|
37
|
+
-s, --slice=number Slice size on POST
|
38
|
+
-i, --headers Show HTTP response headers
|
39
|
+
-v, --verbose Verbose
|
40
|
+
|
41
|
+
```
|
42
|
+
|
43
|
+
## Install
|
44
|
+
|
45
|
+
gem install # not-yet-released
|
46
|
+
|
47
|
+
Gemfile:
|
48
|
+
gem "npolar-api-client-ruby"
|
49
|
+
|
50
|
+
|
51
|
+
## Authentication
|
52
|
+
|
53
|
+
Set the following environmental variables for automatic authentication
|
54
|
+
```
|
55
|
+
NPOLAR_API_USERNAME=username
|
56
|
+
NPOLAR_API_PASSWORD=********
|
57
|
+
```
|
data/bin/npolar-api
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
# Ruby-based command line client for http://api.npolar.no
|
5
|
+
#
|
6
|
+
# For more information: $ ./bin/npolar_api --help
|
7
|
+
# or https://github.com/npolar/npolar-api-client/blob/master/README.md
|
8
|
+
|
9
|
+
require "bundler/setup"
|
10
|
+
require_relative "../lib/npolar/api/client"
|
11
|
+
require_relative "../lib/npolar/api/client/npolar_api_command"
|
12
|
+
|
13
|
+
Npolar::Api::Client::NpolarApiCommand.run
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require "yajl/json_gem"
|
2
|
+
require "hashie"
|
3
|
+
require "typhoeus"
|
4
|
+
require "forwardable"
|
5
|
+
require "uri"
|
6
|
+
|
7
|
+
module Npolar
|
8
|
+
module Api
|
9
|
+
module Client
|
10
|
+
|
11
|
+
VERSION = "0.2.0"
|
12
|
+
|
13
|
+
USER_AGENT = "npolar-api-client-ruby-#{VERSION}/Typhoeus-#{Typhoeus::VERSION}/libcurl-#{`curl --version`.chomp.split(" ")[1]}"
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
require_relative "client/json_api_client"
|
@@ -0,0 +1,567 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require "uri"
|
3
|
+
require "typhoeus"
|
4
|
+
|
5
|
+
class ::Typhoeus::Response
|
6
|
+
alias :status :code
|
7
|
+
|
8
|
+
def uri
|
9
|
+
URI.parse(options[:effective_url])
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
|
14
|
+
class ::Typhoeus::Request
|
15
|
+
|
16
|
+
def uri
|
17
|
+
URI.parse(url)
|
18
|
+
end
|
19
|
+
|
20
|
+
def verb
|
21
|
+
options[:method].to_s.upcase
|
22
|
+
end
|
23
|
+
alias :http_method :verb
|
24
|
+
alias :request_method :verb
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
module Npolar::Api::Client
|
29
|
+
|
30
|
+
# Ruby client for https://api.npolar.no, based on Typhoeus and libcurl
|
31
|
+
# https://github.com/typhoeus/typhoeus
|
32
|
+
class JsonApiClient
|
33
|
+
|
34
|
+
VERSION = "0.10.pre"
|
35
|
+
|
36
|
+
class << self
|
37
|
+
attr_accessor :key
|
38
|
+
end
|
39
|
+
attr_accessor :model, :log, :authorization, :concurrency, :slice, :param, :header
|
40
|
+
attr_reader :uri, :responses, :response, :options
|
41
|
+
|
42
|
+
extend ::Forwardable
|
43
|
+
def_delegators :uri, :scheme, :host, :port, :path
|
44
|
+
|
45
|
+
BASE = "https://api.npolar.no"
|
46
|
+
|
47
|
+
HEADER = { "User-Agent" => Npolar::Api::Client::USER_AGENT,
|
48
|
+
"Content-Type" => "application/json",
|
49
|
+
"Accept" => "application/json",
|
50
|
+
"Accept-Charset" => "UTF-8",
|
51
|
+
"Accept-Encoding" => "gzip,deflate",
|
52
|
+
"Connection" => "keep-alive"
|
53
|
+
}
|
54
|
+
# Typhoeus options => RENAME
|
55
|
+
OPTIONS = { :headers => HEADER,
|
56
|
+
:timeout => nil, # 600 seconds or nil for never
|
57
|
+
:forbid_reuse => true
|
58
|
+
}
|
59
|
+
|
60
|
+
# New client
|
61
|
+
# @param [String | URI] base Base URI for all requests
|
62
|
+
# @param [Hash] options (for Typhoeus)
|
63
|
+
def initialize(base=BASE, options=OPTIONS)
|
64
|
+
# Prepend https://api.npolar.no if base is relative (like /service)
|
65
|
+
if base =~ /^\//
|
66
|
+
path = base
|
67
|
+
base = BASE+path
|
68
|
+
end
|
69
|
+
@base = base
|
70
|
+
unless base.is_a? URI
|
71
|
+
@uri = URI.parse(base)
|
72
|
+
end
|
73
|
+
@options = options
|
74
|
+
init
|
75
|
+
end
|
76
|
+
|
77
|
+
def init
|
78
|
+
@model = Hashie::Mash.new
|
79
|
+
@log = ENV["NPOLAR_ENV"] == "test" ? ::Logger.new("/dev/null") : ::Logger.new(STDERR)
|
80
|
+
@concurrency = 5
|
81
|
+
@slice = 1000
|
82
|
+
@param={}
|
83
|
+
@header={}
|
84
|
+
end
|
85
|
+
|
86
|
+
# All documents
|
87
|
+
def all
|
88
|
+
mash = get_body("_feed", {:fields=>"*"})
|
89
|
+
unless mash.key? "feed"
|
90
|
+
raise "No feed returned"
|
91
|
+
end
|
92
|
+
mash.feed.entries
|
93
|
+
end
|
94
|
+
alias :feed :all
|
95
|
+
|
96
|
+
# Base URI (without trailing slash)
|
97
|
+
def base
|
98
|
+
unless @base.nil?
|
99
|
+
@base.gsub(/\/$/, "")
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# DELETE
|
104
|
+
#
|
105
|
+
def delete(path=nil, param={}, header={})
|
106
|
+
if param.key? "ids"
|
107
|
+
delete_ids(uri, param["ids"])
|
108
|
+
else
|
109
|
+
execute(
|
110
|
+
request(path, :delete, nil, param, header)
|
111
|
+
)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def delete_ids(endpoint, ids)
|
116
|
+
delete_uris(self.class.uris_from_ids(endpoint, ids))
|
117
|
+
end
|
118
|
+
|
119
|
+
def delete_uris(uris)
|
120
|
+
@responses=[]
|
121
|
+
multi_request("DELETE", uris, nil, param, header).run
|
122
|
+
responses
|
123
|
+
end
|
124
|
+
|
125
|
+
# Request header Hash
|
126
|
+
def header
|
127
|
+
options[:headers]
|
128
|
+
# merge!
|
129
|
+
end
|
130
|
+
alias :headers :header
|
131
|
+
|
132
|
+
def http_method
|
133
|
+
@method
|
134
|
+
end
|
135
|
+
alias :verb :http_method
|
136
|
+
|
137
|
+
# Validation errors
|
138
|
+
# @return [Array]
|
139
|
+
def errors(document_or_id)
|
140
|
+
@errors ||= model.merge(document_or_id).errors
|
141
|
+
end
|
142
|
+
|
143
|
+
# deprecated
|
144
|
+
def get_body(uri, param={})
|
145
|
+
@param = param
|
146
|
+
response = get(uri)
|
147
|
+
unless response.success?
|
148
|
+
raise "Could not GET #{uri} status: #{response.code}"
|
149
|
+
end
|
150
|
+
|
151
|
+
begin
|
152
|
+
body = JSON.parse(response.body)
|
153
|
+
if body.is_a? Hash
|
154
|
+
|
155
|
+
if model? and not body.key? "feed"
|
156
|
+
body = model.class.new(body)
|
157
|
+
else
|
158
|
+
body = Hashie::Mash.new(body)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
rescue
|
163
|
+
body = response.body
|
164
|
+
end
|
165
|
+
|
166
|
+
body
|
167
|
+
|
168
|
+
#if response.headers["Content-Type"] =~ /application\/json/
|
169
|
+
# #if model?
|
170
|
+
# JSON.parse(body)
|
171
|
+
# #
|
172
|
+
# #model
|
173
|
+
#else
|
174
|
+
# body
|
175
|
+
#end
|
176
|
+
|
177
|
+
end
|
178
|
+
|
179
|
+
# GET
|
180
|
+
def get(path=nil)
|
181
|
+
if param.key? "ids"
|
182
|
+
get_ids(uri, param["ids"])
|
183
|
+
else
|
184
|
+
request = request(path, :get, nil, param, header)
|
185
|
+
|
186
|
+
request.on_success do |response|
|
187
|
+
if response.headers["Content-Type"] =~ /application\/json/
|
188
|
+
parsed = JSON.parse(response.body)
|
189
|
+
if model?
|
190
|
+
begin
|
191
|
+
# hmm => will loose model!
|
192
|
+
@modelwas = model
|
193
|
+
@model = model.class.new(parsed)
|
194
|
+
@mash = model
|
195
|
+
rescue
|
196
|
+
@model = @modelwas
|
197
|
+
# Parsing only for JSON objects
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
execute(request)
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
def get_ids(endpoint, ids)
|
208
|
+
get_uris(self.class.uris_from_ids(endpoint, ids))
|
209
|
+
end
|
210
|
+
|
211
|
+
def get_uris(uris)
|
212
|
+
@responses=[]
|
213
|
+
multi_request("GET", uris).run
|
214
|
+
responses
|
215
|
+
## set on success =>
|
216
|
+
#json = responses.select {|r| r.success? and r.headers["Content-Type"] =~ /application\/(\w+[+])?json/ }
|
217
|
+
#if json.size == responses.size
|
218
|
+
# responses.map {|r| r.body }
|
219
|
+
#else
|
220
|
+
# raise "Failed "
|
221
|
+
#end
|
222
|
+
end
|
223
|
+
alias :multi_get :get_uris
|
224
|
+
|
225
|
+
# HEAD
|
226
|
+
def head(path=nil, param={}, header={})
|
227
|
+
execute(request(path, :head, nil, param, header))
|
228
|
+
end
|
229
|
+
|
230
|
+
# All ids
|
231
|
+
def ids
|
232
|
+
get_body("_ids").ids
|
233
|
+
end
|
234
|
+
|
235
|
+
# All invalid documents
|
236
|
+
def invalid
|
237
|
+
valid(false)
|
238
|
+
end
|
239
|
+
|
240
|
+
# Model?
|
241
|
+
def model?
|
242
|
+
not @model.nil?
|
243
|
+
end
|
244
|
+
|
245
|
+
# POST
|
246
|
+
# @param [Array, Hash, String] body
|
247
|
+
def post(body, path=nil, param={}, header={})
|
248
|
+
if header["Content-Type"] =~ /application\/(\w+[+])?json/
|
249
|
+
chunk_save(path, "POST", body, param, header)
|
250
|
+
else
|
251
|
+
execute(
|
252
|
+
request(path, :post, body, param, header)
|
253
|
+
)
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
# PUT
|
258
|
+
def put(body, path=nil, param={}, header={})
|
259
|
+
execute(
|
260
|
+
request(path, :put, body, param, header)
|
261
|
+
)
|
262
|
+
end
|
263
|
+
|
264
|
+
def status
|
265
|
+
response.code
|
266
|
+
end
|
267
|
+
|
268
|
+
def uris
|
269
|
+
ids.map {|id| base+"/"+id }
|
270
|
+
end
|
271
|
+
|
272
|
+
# All valid documents
|
273
|
+
def valid(condition=true)
|
274
|
+
all.select {|d| condition == valid?(d) }.map {|d| model.class.new(d)}
|
275
|
+
end
|
276
|
+
|
277
|
+
# Valid?
|
278
|
+
def valid?(document_or_id)
|
279
|
+
# FIXME Hashie::Mash will always respond to #valid?
|
280
|
+
if not model? or not model.respond_to?(:valid?)
|
281
|
+
return true
|
282
|
+
end
|
283
|
+
|
284
|
+
validator = model.class.new(document_or_id)
|
285
|
+
|
286
|
+
# Return true if validator is a Hash with errors key !
|
287
|
+
# FIXME Hashie::Mash will always respond to #valid?
|
288
|
+
if validator.key? :valid? or validator.key? :errors
|
289
|
+
return true
|
290
|
+
end
|
291
|
+
|
292
|
+
valid = validator.valid?
|
293
|
+
|
294
|
+
if validator.errors.nil?
|
295
|
+
return true
|
296
|
+
end
|
297
|
+
|
298
|
+
@errors = validator.errors # store to avoid revalidating
|
299
|
+
valid = case valid
|
300
|
+
when true, nil
|
301
|
+
true
|
302
|
+
when false
|
303
|
+
false
|
304
|
+
end
|
305
|
+
valid
|
306
|
+
end
|
307
|
+
|
308
|
+
def username
|
309
|
+
# export NPOLAR_HTTP_USERNAME=http_username
|
310
|
+
@username ||= ENV["NPOLAR_API_USERNAME"]
|
311
|
+
end
|
312
|
+
|
313
|
+
def username=(username)
|
314
|
+
@username=username
|
315
|
+
end
|
316
|
+
|
317
|
+
def password
|
318
|
+
# export NPOLAR_HTTP_PASSWORD=http_password
|
319
|
+
@password ||= ENV["NPOLAR_API_PASSWORD"]
|
320
|
+
end
|
321
|
+
|
322
|
+
def password=(password)
|
323
|
+
@password=password
|
324
|
+
end
|
325
|
+
|
326
|
+
def execute(request=nil)
|
327
|
+
log.debug log_message(request)
|
328
|
+
@response = request.run
|
329
|
+
end
|
330
|
+
|
331
|
+
# @return []
|
332
|
+
def request(path=nil, method=:get, body=nil, params={}, headers={})
|
333
|
+
|
334
|
+
if path =~ /^http(s)?[:]\/\//
|
335
|
+
# Absolute URI
|
336
|
+
uri = path
|
337
|
+
|
338
|
+
elsif path.nil?
|
339
|
+
# Use base URI if path is nil
|
340
|
+
uri = base
|
341
|
+
|
342
|
+
elsif path =~ /^\/\w+/
|
343
|
+
# Support /relative URIs by prepending base
|
344
|
+
uri = base+path
|
345
|
+
|
346
|
+
elsif path =~ /^\w+/ and base =~ /^http(s)?[:]\/\//
|
347
|
+
uri = base+"/"+path
|
348
|
+
else
|
349
|
+
# Invalid URI
|
350
|
+
raise ArgumentError, "Path is invalid: #{path}"
|
351
|
+
end
|
352
|
+
|
353
|
+
unless uri.is_a? URI
|
354
|
+
uri = URI.parse(uri)
|
355
|
+
end
|
356
|
+
|
357
|
+
@uri = uri
|
358
|
+
@param = param
|
359
|
+
@header = headers
|
360
|
+
method = method.downcase.to_sym
|
361
|
+
|
362
|
+
context = { method: method,
|
363
|
+
body: body,
|
364
|
+
params: params,
|
365
|
+
headers: headers
|
366
|
+
}
|
367
|
+
if true == authorization or [:delete, :post, :put].include? method
|
368
|
+
context[:userpwd] = "#{username}:#{password}"
|
369
|
+
end
|
370
|
+
|
371
|
+
request = Typhoeus::Request.new(uri.to_s, context)
|
372
|
+
|
373
|
+
request.on_complete do |response|
|
374
|
+
on_complete.call(response)
|
375
|
+
end
|
376
|
+
|
377
|
+
request.on_failure do |response|
|
378
|
+
on_failure.call(response)
|
379
|
+
end
|
380
|
+
|
381
|
+
request.on_success do |response|
|
382
|
+
on_success.call(response)
|
383
|
+
end
|
384
|
+
|
385
|
+
@request = request
|
386
|
+
|
387
|
+
request
|
388
|
+
|
389
|
+
end
|
390
|
+
|
391
|
+
def on_failure
|
392
|
+
@on_failure ||= lambda {|response|
|
393
|
+
if response.code == 0
|
394
|
+
# No response, something's wrong.
|
395
|
+
log.error "#{request.verb} #{request.uri.path} failed with message: #{response.return_message}"
|
396
|
+
elsif response.timed_out?
|
397
|
+
log.error "#{request.verb} #{request.uri.path} timed out in #{response.total_time} seconds"
|
398
|
+
else
|
399
|
+
log.error log_message(response)
|
400
|
+
end
|
401
|
+
}
|
402
|
+
end
|
403
|
+
|
404
|
+
def on_complete
|
405
|
+
@on_complete ||= lambda {|response|} #noop
|
406
|
+
end
|
407
|
+
|
408
|
+
def on_success
|
409
|
+
@on_success ||= lambda {|response|
|
410
|
+
log.info log_message(response)
|
411
|
+
}
|
412
|
+
end
|
413
|
+
|
414
|
+
#def on_complete=(on_complete_lambda)
|
415
|
+
# if @on_complete.nil?
|
416
|
+
# @on_complete = []
|
417
|
+
# end
|
418
|
+
# @on_complete << on_complete_lambda
|
419
|
+
#end
|
420
|
+
|
421
|
+
protected
|
422
|
+
|
423
|
+
# @return [Array] ids
|
424
|
+
def self.fetch_ids(uri)
|
425
|
+
client = self.new(uri)
|
426
|
+
client.model = nil
|
427
|
+
|
428
|
+
response = client.get
|
429
|
+
#if 200 == response.code
|
430
|
+
#
|
431
|
+
#end
|
432
|
+
|
433
|
+
idlist = JSON.parse(response.body)
|
434
|
+
|
435
|
+
if idlist.key? "feed" and idlist["feed"].key? "entries"
|
436
|
+
|
437
|
+
ids = idlist["feed"]["entries"].map {|d|
|
438
|
+
d["id"]
|
439
|
+
}
|
440
|
+
|
441
|
+
elsif idlist.key? "ids"
|
442
|
+
|
443
|
+
ids = idlist["ids"]
|
444
|
+
|
445
|
+
else
|
446
|
+
raise "Cannot fetch ids"
|
447
|
+
end
|
448
|
+
end
|
449
|
+
|
450
|
+
# @return [Array] URIs
|
451
|
+
def self.uris_from_ids(base, ids)
|
452
|
+
|
453
|
+
unless ids.is_a? Array
|
454
|
+
if ids =~ /^http(s)?[:]\/\//
|
455
|
+
ids = fetch_ids(ids)
|
456
|
+
else
|
457
|
+
raise "Can only fetch ids via HTTP"
|
458
|
+
end
|
459
|
+
end
|
460
|
+
|
461
|
+
unless base.is_a? URI
|
462
|
+
base = URI.parse(base)
|
463
|
+
end
|
464
|
+
|
465
|
+
ids.map {|id|
|
466
|
+
path = base.path+"/"+id
|
467
|
+
uri = base.dup
|
468
|
+
uri.path = path
|
469
|
+
uri
|
470
|
+
}
|
471
|
+
|
472
|
+
end
|
473
|
+
|
474
|
+
def hydra
|
475
|
+
@hydra ||= Typhoeus::Hydra.new(max_concurrency: concurrency)
|
476
|
+
end
|
477
|
+
|
478
|
+
# Prepare and queue a multi request
|
479
|
+
#
|
480
|
+
# @return [#run]
|
481
|
+
def multi_request(method, paths, body=nil, param=nil, header=nil)
|
482
|
+
@multi = true
|
483
|
+
|
484
|
+
# Response storage, if not already set
|
485
|
+
if @responses.nil?
|
486
|
+
@responses = []
|
487
|
+
end
|
488
|
+
|
489
|
+
# Handle one or many paths
|
490
|
+
if paths.is_a? String or paths.is_a? URI
|
491
|
+
paths = [paths]
|
492
|
+
end
|
493
|
+
|
494
|
+
# Handle (URI) objects
|
495
|
+
paths = paths.map {|p| p.to_s }
|
496
|
+
|
497
|
+
log.debug "Queueing multi-#{method} requests, concurrency: #{concurrency}, path(s): #{ paths.size == 1 ? paths[0]: paths.size }"
|
498
|
+
|
499
|
+
paths.each do | path |
|
500
|
+
|
501
|
+
multi_request = request(path, method.downcase.to_sym, body, param, header)
|
502
|
+
multi_request.on_complete do | response |
|
503
|
+
log.debug "Multi-#{method} [#{paths.size}]: "+log_message(response)
|
504
|
+
@responses << response
|
505
|
+
end
|
506
|
+
hydra.queue(multi_request)
|
507
|
+
end
|
508
|
+
hydra
|
509
|
+
end
|
510
|
+
alias :queue :multi_request
|
511
|
+
|
512
|
+
# Slice Array of documents into chunks of #slice size and queue up for POST or PUT
|
513
|
+
# @return [Array] responses
|
514
|
+
def chunk_save(path=nil, method="POST", docs, param, header)
|
515
|
+
@multi = true
|
516
|
+
|
517
|
+
if path.nil?
|
518
|
+
path = uri
|
519
|
+
end
|
520
|
+
|
521
|
+
unless docs.is_a? Array
|
522
|
+
docs = JSON.parse(docs)
|
523
|
+
end
|
524
|
+
|
525
|
+
if docs.is_a? Hash
|
526
|
+
docs = [docs]
|
527
|
+
end
|
528
|
+
if slice > docs.size
|
529
|
+
log.debug "Slicing #{docs.size} documents into #{(docs.size.to_f/slice.to_f).round} chunks of #{slice} each"
|
530
|
+
end
|
531
|
+
docs.each_slice(slice) do | chunk |
|
532
|
+
queue(method, path, chunk.to_json, param, header)
|
533
|
+
end
|
534
|
+
hydra.run
|
535
|
+
|
536
|
+
# @todo => on complete
|
537
|
+
successes = @responses.select {|r| (200..299).include? r.code }
|
538
|
+
if successes.size > 0
|
539
|
+
|
540
|
+
if docs.size < slice
|
541
|
+
log.info "Saved #{docs.size} document(s) using #{@responses.size} #{method} request(s). Concurrency: #{concurrency}"
|
542
|
+
else
|
543
|
+
log.info "Saved #{docs.size} documents, sliced into chunks of #{slice} using #{@responses.size} #{method} requests. Concurrency: #{concurrency}"
|
544
|
+
end
|
545
|
+
else
|
546
|
+
failures = @responses.reject {|r| (200..299).include? r.code }
|
547
|
+
log.debug "#chunk_save error in #{failures.size}/#{responses.size} requests"
|
548
|
+
end
|
549
|
+
|
550
|
+
@responses
|
551
|
+
end
|
552
|
+
|
553
|
+
|
554
|
+
def log_message(r)
|
555
|
+
if r.is_a? Typhoeus::Request
|
556
|
+
request = r
|
557
|
+
"#{request.http_method} #{scheme}://#{host}:#{port}#{path} [#{self.class.name}] #{param} #{header}"
|
558
|
+
else
|
559
|
+
response = r
|
560
|
+
|
561
|
+
"#{response.code} #{response.request.http_method} #{response.request.url} [#{self.class.name}] #{response.total_time} #{response.body.bytesize} #{response.body[0..255]}"
|
562
|
+
end
|
563
|
+
end
|
564
|
+
|
565
|
+
end
|
566
|
+
|
567
|
+
end
|