queuery_client 0.8.0 → 1.0.3

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