queuery_client 0.8.0 → 1.0.3
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 +5 -5
- data/lib/queuery_client.rb +2 -1
- data/lib/queuery_client/client.rb +27 -2
- data/lib/queuery_client/configuration.rb +14 -5
- data/lib/queuery_client/data_file.rb +32 -0
- data/lib/queuery_client/data_file_bundle.rb +21 -0
- data/lib/queuery_client/s3_data_file.rb +18 -0
- data/lib/queuery_client/s3_data_file_bundle.rb +28 -0
- data/lib/queuery_client/url_data_file.rb +27 -0
- data/lib/queuery_client/url_data_file_bundle.rb +33 -0
- data/lib/queuery_client/version.rb +1 -1
- data/queuery_client.gemspec +3 -2
- metadata +28 -11
- data/lib/queuery_client/queuery_data_file_bundle.rb +0 -19
- data/queuery_client.rb +0 -64
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: a5aa7c59714b2972a888c845beaaf38e220fdc0173cd746a0a73f510a1c69afd
|
4
|
+
data.tar.gz: c876ff24ba9b2d6f489d480de5fc55b74d08ab2aa0229c4cb2a74ca0914053f9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0c154295e3c4c676de39ee43b544ec458285044c0d797f7ec199c39589023019c7f407623745a53d2da6016a9eb1b1594240e2a1f74d0093025e9d3d73d0ab56
|
7
|
+
data.tar.gz: f7340f67c56865d1c179d062801087931db880a6f9ccbc3f5d3109438ab277a2902000e253bf6d45f8ceebf47b91f2d27fb6709e2fe305f90201d6e9fba206c7
|
data/lib/queuery_client.rb
CHANGED
@@ -3,7 +3,8 @@ require "queuery_client/configuration"
|
|
3
3
|
require "queuery_client/basic_auth_garage_client"
|
4
4
|
require "queuery_client/query_error"
|
5
5
|
require "queuery_client/client"
|
6
|
-
require "queuery_client/
|
6
|
+
require "queuery_client/url_data_file_bundle"
|
7
|
+
require "queuery_client/s3_data_file_bundle"
|
7
8
|
|
8
9
|
module QueueryClient
|
9
10
|
class << self
|
@@ -7,6 +7,7 @@ module QueueryClient
|
|
7
7
|
def execute_query(select_stmt, values)
|
8
8
|
garage_client.post("/v1/queries", q: select_stmt, values: values)
|
9
9
|
end
|
10
|
+
alias start_query execute_query
|
10
11
|
|
11
12
|
def get_query(id)
|
12
13
|
garage_client.get("/v1/queries/#{id}", fields: '__default__,s3_prefix')
|
@@ -32,7 +33,7 @@ module QueueryClient
|
|
32
33
|
query = query_and_wait(select_stmt, values)
|
33
34
|
case query.status
|
34
35
|
when 'success'
|
35
|
-
|
36
|
+
UrlDataFileBundle.new(
|
36
37
|
query.data_file_urls,
|
37
38
|
s3_prefix: query.s3_prefix,
|
38
39
|
)
|
@@ -41,13 +42,21 @@ module QueueryClient
|
|
41
42
|
end
|
42
43
|
end
|
43
44
|
|
45
|
+
# poll_result returns the results only if the query has already successed.
|
46
|
+
def poll_result(id)
|
47
|
+
query = get_query(id)
|
48
|
+
get_query_result(query)
|
49
|
+
end
|
50
|
+
|
44
51
|
def garage_client
|
45
52
|
@garage_client ||= BasicAuthGarageClient.new(
|
46
53
|
endpoint: options.endpoint,
|
47
54
|
path_prefix: '/',
|
48
55
|
login: options.token,
|
49
56
|
password: options.token_secret
|
50
|
-
)
|
57
|
+
).tap do |client|
|
58
|
+
client.headers['Host'] = options.host_header if options.host_header
|
59
|
+
end
|
51
60
|
end
|
52
61
|
|
53
62
|
def options
|
@@ -57,5 +66,21 @@ module QueueryClient
|
|
57
66
|
def default_options
|
58
67
|
QueueryClient.configuration
|
59
68
|
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def get_query_result(query)
|
73
|
+
case query.status
|
74
|
+
when 'pending', 'running'
|
75
|
+
nil
|
76
|
+
when 'success'
|
77
|
+
UrlDataFileBundle.new(
|
78
|
+
query.data_file_urls,
|
79
|
+
s3_prefix: query.s3_prefix,
|
80
|
+
)
|
81
|
+
when 'failure'
|
82
|
+
raise QueryError.new(query.error)
|
83
|
+
end
|
84
|
+
end
|
60
85
|
end
|
61
86
|
end
|
@@ -1,5 +1,8 @@
|
|
1
1
|
module QueueryClient
|
2
2
|
class Configuration
|
3
|
+
REQUIRED_KEYS = [:endpoint, :token, :token_secret]
|
4
|
+
OPTIONAL_KEYS = [:host_header]
|
5
|
+
|
3
6
|
def initialize(options = {})
|
4
7
|
@options = options
|
5
8
|
end
|
@@ -12,11 +15,7 @@ module QueueryClient
|
|
12
15
|
@options = nil
|
13
16
|
end
|
14
17
|
|
15
|
-
|
16
|
-
:endpoint,
|
17
|
-
:token,
|
18
|
-
:token_secret,
|
19
|
-
].each do |key|
|
18
|
+
REQUIRED_KEYS.each do |key|
|
20
19
|
define_method(key) do
|
21
20
|
options.fetch(key)
|
22
21
|
end
|
@@ -26,6 +25,16 @@ module QueueryClient
|
|
26
25
|
end
|
27
26
|
end
|
28
27
|
|
28
|
+
OPTIONAL_KEYS.each do |key|
|
29
|
+
define_method(key) do
|
30
|
+
options[key]
|
31
|
+
end
|
32
|
+
|
33
|
+
define_method("#{key}=") do |value|
|
34
|
+
options[key] = value
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
29
38
|
def merge(other)
|
30
39
|
Configuration.new(to_h.merge(other.to_h))
|
31
40
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'redshift_csv_file'
|
2
|
+
require 'zlib'
|
3
|
+
|
4
|
+
module QueueryClient
|
5
|
+
class DataFile
|
6
|
+
def data_object?
|
7
|
+
/\.csv(?:\.|\z)/ =~ File.basename(key)
|
8
|
+
end
|
9
|
+
|
10
|
+
def gzipped_object?
|
11
|
+
File.extname(key) == '.gz'
|
12
|
+
end
|
13
|
+
|
14
|
+
def each_row(&block)
|
15
|
+
return enum_for(:each_row) if !block_given?
|
16
|
+
|
17
|
+
f = open
|
18
|
+
begin
|
19
|
+
if gzipped_object?
|
20
|
+
f = Zlib::GzipReader.new(f)
|
21
|
+
end
|
22
|
+
RedshiftCsvFile.new(f).each do |row|
|
23
|
+
yield row
|
24
|
+
end
|
25
|
+
ensure
|
26
|
+
f.close
|
27
|
+
end
|
28
|
+
|
29
|
+
self
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module QueueryClient
|
2
|
+
class DataFileBundle
|
3
|
+
# abstract data_files :: [DataFile]
|
4
|
+
|
5
|
+
def each_row(&block)
|
6
|
+
return enum_for(:each_row) if !block_given?
|
7
|
+
|
8
|
+
data_files.each do |file|
|
9
|
+
if file.data_object?
|
10
|
+
file.each_row do |row|
|
11
|
+
yield row
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
self
|
17
|
+
end
|
18
|
+
|
19
|
+
alias each each_row
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'queuery_client/data_file'
|
2
|
+
require 'forwardable'
|
3
|
+
|
4
|
+
module QueueryClient
|
5
|
+
class S3DataFile < DataFile
|
6
|
+
extend Forwardable
|
7
|
+
|
8
|
+
def initialize(object)
|
9
|
+
@object = object
|
10
|
+
end
|
11
|
+
|
12
|
+
def_delegators '@object', :url, :key, :presigned_url
|
13
|
+
|
14
|
+
def open
|
15
|
+
@object.get.body
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'queuery_client/data_file_bundle'
|
2
|
+
require 'queuery_client/s3_data_file'
|
3
|
+
require 'aws-sdk-s3'
|
4
|
+
require 'logger'
|
5
|
+
|
6
|
+
module QueueryClient
|
7
|
+
class S3DataFileBundle < DataFileBundle
|
8
|
+
def initialize(bucket, prefix, s3_client: nil, logger: Logger.new($stderr))
|
9
|
+
@s3_client = s3_client || Aws::S3::Client.new # Use env to inject credentials
|
10
|
+
@bucket = bucket
|
11
|
+
@prefix = prefix
|
12
|
+
@logger = logger
|
13
|
+
end
|
14
|
+
|
15
|
+
attr_reader :bucket
|
16
|
+
attr_reader :prefix
|
17
|
+
attr_reader :logger
|
18
|
+
|
19
|
+
def url
|
20
|
+
"s3://#{@bucket}/#{@prefix}"
|
21
|
+
end
|
22
|
+
|
23
|
+
def data_files
|
24
|
+
b = Aws::S3::Resource.new(client: @s3_client).bucket(@bucket)
|
25
|
+
b.objects(prefix: @prefix).map {|obj| S3DataFile.new(obj) }
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'queuery_client/data_file'
|
2
|
+
require 'net/http'
|
3
|
+
require 'stringio'
|
4
|
+
|
5
|
+
module QueueryClient
|
6
|
+
class UrlDataFile < DataFile
|
7
|
+
def initialize(url)
|
8
|
+
@url = url
|
9
|
+
end
|
10
|
+
|
11
|
+
attr_reader :url
|
12
|
+
|
13
|
+
def key
|
14
|
+
@url.path
|
15
|
+
end
|
16
|
+
|
17
|
+
def open
|
18
|
+
http = Net::HTTP.new(@url.host, @url.port)
|
19
|
+
http.use_ssl = (@url.scheme.downcase == 'https')
|
20
|
+
content = http.start {
|
21
|
+
res = http.get(@url.request_uri)
|
22
|
+
res.body
|
23
|
+
}
|
24
|
+
StringIO.new(content)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'queuery_client/data_file_bundle'
|
2
|
+
require 'queuery_client/url_data_file'
|
3
|
+
require 'uri'
|
4
|
+
require 'logger'
|
5
|
+
|
6
|
+
module QueueryClient
|
7
|
+
class UrlDataFileBundle < DataFileBundle
|
8
|
+
def initialize(urls, s3_prefix:, logger: Logger.new($stderr))
|
9
|
+
raise ArgumentError, 'no URL given' if urls.empty?
|
10
|
+
@data_files = urls.map {|url| UrlDataFile.new(URI.parse(url)) }
|
11
|
+
@s3_prefix = s3_prefix
|
12
|
+
@logger = logger
|
13
|
+
end
|
14
|
+
|
15
|
+
attr_reader :data_files
|
16
|
+
attr_reader :s3_prefix
|
17
|
+
attr_reader :logger
|
18
|
+
|
19
|
+
def url
|
20
|
+
uri = data_files.first.url.dup
|
21
|
+
uri.query = nil
|
22
|
+
uri.path = File.dirname(uri.path)
|
23
|
+
uri.to_s
|
24
|
+
end
|
25
|
+
|
26
|
+
def direct(bucket_opts = {}, bundle_opts = {})
|
27
|
+
s3_uri = URI.parse(s3_prefix)
|
28
|
+
bucket = s3_uri.host
|
29
|
+
prefix = s3_uri.path[1..-1] # trim heading slash
|
30
|
+
S3DataFileBundle.new(bucket, prefix)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/queuery_client.gemspec
CHANGED
@@ -29,8 +29,9 @@ Gem::Specification.new do |spec|
|
|
29
29
|
spec.require_paths = ["lib"]
|
30
30
|
|
31
31
|
spec.add_dependency 'garage_client'
|
32
|
-
spec.add_dependency
|
32
|
+
spec.add_dependency 'redshift_csv_file'
|
33
|
+
spec.add_dependency 'aws-sdk-s3'
|
33
34
|
spec.add_development_dependency "bundler", "~> 1.13"
|
34
|
-
spec.add_development_dependency "rake", "~>
|
35
|
+
spec.add_development_dependency "rake", "~> 12.0"
|
35
36
|
spec.add_development_dependency "pry"
|
36
37
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: queuery_client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Hidekazu Kobayashi
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-08-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: garage_client
|
@@ -25,19 +25,33 @@ dependencies:
|
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: redshift_csv_file
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
33
|
+
version: '0'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: aws-sdk-s3
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
56
|
name: bundler
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -58,14 +72,14 @@ dependencies:
|
|
58
72
|
requirements:
|
59
73
|
- - "~>"
|
60
74
|
- !ruby/object:Gem::Version
|
61
|
-
version: '
|
75
|
+
version: '12.0'
|
62
76
|
type: :development
|
63
77
|
prerelease: false
|
64
78
|
version_requirements: !ruby/object:Gem::Requirement
|
65
79
|
requirements:
|
66
80
|
- - "~>"
|
67
81
|
- !ruby/object:Gem::Version
|
68
|
-
version: '
|
82
|
+
version: '12.0'
|
69
83
|
- !ruby/object:Gem::Dependency
|
70
84
|
name: pry
|
71
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -97,11 +111,15 @@ files:
|
|
97
111
|
- lib/queuery_client/basic_auth_garage_client.rb
|
98
112
|
- lib/queuery_client/client.rb
|
99
113
|
- lib/queuery_client/configuration.rb
|
114
|
+
- lib/queuery_client/data_file.rb
|
115
|
+
- lib/queuery_client/data_file_bundle.rb
|
100
116
|
- lib/queuery_client/query_error.rb
|
101
|
-
- lib/queuery_client/
|
117
|
+
- lib/queuery_client/s3_data_file.rb
|
118
|
+
- lib/queuery_client/s3_data_file_bundle.rb
|
119
|
+
- lib/queuery_client/url_data_file.rb
|
120
|
+
- lib/queuery_client/url_data_file_bundle.rb
|
102
121
|
- lib/queuery_client/version.rb
|
103
122
|
- queuery_client.gemspec
|
104
|
-
- queuery_client.rb
|
105
123
|
homepage: https://github.com/bricolages/queuery_client
|
106
124
|
licenses:
|
107
125
|
- MIT
|
@@ -122,8 +140,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
122
140
|
- !ruby/object:Gem::Version
|
123
141
|
version: '0'
|
124
142
|
requirements: []
|
125
|
-
|
126
|
-
rubygems_version: 2.6.14
|
143
|
+
rubygems_version: 3.1.2
|
127
144
|
signing_key:
|
128
145
|
specification_version: 4
|
129
146
|
summary: Client library for Queuery Redshift HTTP API
|
@@ -1,19 +0,0 @@
|
|
1
|
-
require 'redshift_connector/url_data_file_bundle'
|
2
|
-
|
3
|
-
module QueueryClient
|
4
|
-
class QueueryDataFileBundle < RedshiftConnector::UrlDataFileBundle
|
5
|
-
def initialize(url, s3_prefix:, **args)
|
6
|
-
super(url, **args)
|
7
|
-
@s3_prefix = s3_prefix
|
8
|
-
end
|
9
|
-
|
10
|
-
attr_reader :s3_prefix
|
11
|
-
|
12
|
-
def url
|
13
|
-
uri = data_files.first.url.dup
|
14
|
-
uri.query = nil
|
15
|
-
uri.path = File.dirname(uri.path)
|
16
|
-
uri.to_s
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
data/queuery_client.rb
DELETED
@@ -1,64 +0,0 @@
|
|
1
|
-
require 'uri'
|
2
|
-
require 'net/http'
|
3
|
-
|
4
|
-
class QueueryClient
|
5
|
-
class << self
|
6
|
-
attr_accessor :host
|
7
|
-
attr_accessor :port
|
8
|
-
|
9
|
-
def query(select_stmt)
|
10
|
-
client = new(
|
11
|
-
host: host,
|
12
|
-
port: port,
|
13
|
-
)
|
14
|
-
|
15
|
-
res = client.query_and_wait(select_stmt)
|
16
|
-
RedshiftConnector::UrlDataFileBundle.new(res['data_objects'])
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
def initialize(host:, port: 80)
|
21
|
-
@host = host
|
22
|
-
@port = port
|
23
|
-
end
|
24
|
-
|
25
|
-
def request(req)
|
26
|
-
res = Net::HTTP.new(@host, @port).request(req)
|
27
|
-
JSON.parse(res.body)
|
28
|
-
end
|
29
|
-
|
30
|
-
def request_post(path, params = {})
|
31
|
-
post_req = Net::HTTP::Post.new(path)
|
32
|
-
post_req.form_data = params
|
33
|
-
request(post_req)
|
34
|
-
end
|
35
|
-
|
36
|
-
def request_get(path)
|
37
|
-
get_req = Net::HTTP::Get.new(path)
|
38
|
-
request(get_req)
|
39
|
-
end
|
40
|
-
|
41
|
-
def query(select_stmt)
|
42
|
-
request_post('/queries', { q: select_stmt })
|
43
|
-
end
|
44
|
-
|
45
|
-
def status(job_id)
|
46
|
-
request_get("/queries/#{job_id}")
|
47
|
-
end
|
48
|
-
|
49
|
-
def wait_for(job_id)
|
50
|
-
loop do
|
51
|
-
res = status(job_id)
|
52
|
-
case res['status']
|
53
|
-
when 'success', 'failed'
|
54
|
-
return res
|
55
|
-
end
|
56
|
-
sleep 1
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
def query_and_wait(select_stmt)
|
61
|
-
res = query(select_stmt)
|
62
|
-
wait_for(res['job_id'])
|
63
|
-
end
|
64
|
-
end
|