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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: d9bdfe61f21a506cef4c09057e13510305826234
4
- data.tar.gz: '068d8a19aea9e7a13dd1c2b997848363e5a66217'
2
+ SHA256:
3
+ metadata.gz: a5aa7c59714b2972a888c845beaaf38e220fdc0173cd746a0a73f510a1c69afd
4
+ data.tar.gz: c876ff24ba9b2d6f489d480de5fc55b74d08ab2aa0229c4cb2a74ca0914053f9
5
5
  SHA512:
6
- metadata.gz: 6cdc5d84c7ce94b3ed1c835b00f20d7893b0f08776eac198bb58951bf2def275692a3fd87f4324bbb033150c26984c50f9918bce201f4aa9c7137a33926b66ea
7
- data.tar.gz: 50bc3ebad07bd41bed5c1a4c554155478d554a93c2a2887eb7693c319d0ab9d7cb7d52266121285d81139f4a30be9079144aed9d47debb92e2805439065fc086
6
+ metadata.gz: 0c154295e3c4c676de39ee43b544ec458285044c0d797f7ec199c39589023019c7f407623745a53d2da6016a9eb1b1594240e2a1f74d0093025e9d3d73d0ab56
7
+ data.tar.gz: f7340f67c56865d1c179d062801087931db880a6f9ccbc3f5d3109438ab277a2902000e253bf6d45f8ceebf47b91f2d27fb6709e2fe305f90201d6e9fba206c7
@@ -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/queuery_data_file_bundle"
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
- QueueryDataFileBundle.new(
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
@@ -1,3 +1,3 @@
1
1
  module QueueryClient
2
- VERSION = "0.8.0"
2
+ VERSION = "1.0.3"
3
3
  end
@@ -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 "redshift-connector-data_file", ">= 7.1"
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", "~> 10.0"
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.8.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: 2018-03-22 00:00:00.000000000 Z
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: redshift-connector-data_file
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: '7.1'
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: '7.1'
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: '10.0'
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: '10.0'
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/queuery_data_file_bundle.rb
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
- rubyforge_project:
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
@@ -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