td-client 1.0.0-java

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.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/data/ca-bundle.crt +3448 -0
  3. data/lib/td-client.rb +1 -0
  4. data/lib/td/client.rb +606 -0
  5. data/lib/td/client/api.rb +707 -0
  6. data/lib/td/client/api/access_control.rb +74 -0
  7. data/lib/td/client/api/account.rb +45 -0
  8. data/lib/td/client/api/bulk_import.rb +184 -0
  9. data/lib/td/client/api/bulk_load.rb +172 -0
  10. data/lib/td/client/api/database.rb +50 -0
  11. data/lib/td/client/api/export.rb +38 -0
  12. data/lib/td/client/api/import.rb +38 -0
  13. data/lib/td/client/api/job.rb +390 -0
  14. data/lib/td/client/api/partial_delete.rb +27 -0
  15. data/lib/td/client/api/result.rb +46 -0
  16. data/lib/td/client/api/schedule.rb +120 -0
  17. data/lib/td/client/api/server_status.rb +21 -0
  18. data/lib/td/client/api/table.rb +132 -0
  19. data/lib/td/client/api/user.rb +134 -0
  20. data/lib/td/client/api_error.rb +37 -0
  21. data/lib/td/client/compat_gzip_reader.rb +22 -0
  22. data/lib/td/client/model.rb +816 -0
  23. data/lib/td/client/version.rb +5 -0
  24. data/lib/td/core_ext/openssl/ssl/sslcontext/set_params.rb +18 -0
  25. data/spec/spec_helper.rb +63 -0
  26. data/spec/td/client/access_control_api_spec.rb +37 -0
  27. data/spec/td/client/account_api_spec.rb +34 -0
  28. data/spec/td/client/api_error_spec.rb +77 -0
  29. data/spec/td/client/api_spec.rb +269 -0
  30. data/spec/td/client/api_ssl_connection_spec.rb +109 -0
  31. data/spec/td/client/bulk_import_spec.rb +199 -0
  32. data/spec/td/client/bulk_load_spec.rb +401 -0
  33. data/spec/td/client/db_api_spec.rb +123 -0
  34. data/spec/td/client/export_api_spec.rb +51 -0
  35. data/spec/td/client/import_api_spec.rb +148 -0
  36. data/spec/td/client/job_api_spec.rb +833 -0
  37. data/spec/td/client/model_job_spec.rb +136 -0
  38. data/spec/td/client/model_schedule_spec.rb +26 -0
  39. data/spec/td/client/model_schema_spec.rb +134 -0
  40. data/spec/td/client/partial_delete_api_spec.rb +58 -0
  41. data/spec/td/client/result_api_spec.rb +77 -0
  42. data/spec/td/client/sched_api_spec.rb +109 -0
  43. data/spec/td/client/server_status_api_spec.rb +25 -0
  44. data/spec/td/client/spec_resources.rb +99 -0
  45. data/spec/td/client/table_api_spec.rb +226 -0
  46. data/spec/td/client/user_api_spec.rb +118 -0
  47. data/spec/td/client_sched_spec.rb +79 -0
  48. data/spec/td/client_spec.rb +46 -0
  49. metadata +271 -0
@@ -0,0 +1,109 @@
1
+ require 'spec_helper'
2
+ require 'td/client/api'
3
+ require 'logger'
4
+ require 'webrick'
5
+ require 'webrick/https'
6
+
7
+ # Workaround for https://github.com/jruby/jruby-openssl/issues/78
8
+ # With recent JRuby + jruby-openssl, X509CRL#extentions_to_text causes
9
+ # StringIndexOOBException when we try to dump SSL Server Certificate.
10
+ # when one of extensions has "" as value.
11
+ # This hack is from httpclient https://github.com/nahi/httpclient/blob/master/lib/httpclient/util.rb#L27-L46
12
+ if defined? JRUBY_VERSION
13
+ require 'openssl'
14
+ require 'java'
15
+ module OpenSSL
16
+ module X509
17
+ class Certificate
18
+ java_import 'java.security.cert.Certificate'
19
+ java_import 'java.security.cert.CertificateFactory'
20
+ java_import 'java.io.ByteArrayInputStream'
21
+ def to_text
22
+ cf = CertificateFactory.getInstance('X.509')
23
+ cf.generateCertificate(ByteArrayInputStream.new(self.to_der.to_java_bytes)).toString
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+
30
+ describe 'API SSL connection' do
31
+ DIR = File.dirname(File.expand_path(__FILE__))
32
+
33
+ after :each do
34
+ @server.shutdown if @server
35
+ end
36
+
37
+ it 'should fail to connect SSLv3 only server' do
38
+ @server = setup_server(:SSLv3)
39
+ api = API.new(nil, :endpoint => "https://localhost:#{@serverport}", :retry_post_requests => false)
40
+ api.ssl_ca_file = File.join(DIR, 'ca-all.cert')
41
+ expect {
42
+ api.delete_database('no_such_database')
43
+ }.to raise_error OpenSSL::SSL::SSLError
44
+ end
45
+
46
+ # 1.8.7's net/http does not call SSLContext#set_params. Use 1.8.7 where you don't care security.
47
+ if RUBY_VERSION >= '1.9.0'
48
+ it 'should success to connect TLSv1 only server' do
49
+ @server = setup_server(:TLSv1)
50
+ api = API.new(nil, :endpoint => "https://localhost:#{@serverport}", :retry_post_requests => false)
51
+ api.ssl_ca_file = File.join(DIR, 'ca-all.cert')
52
+ expect {
53
+ api.delete_database('no_such_database')
54
+ }.to raise_error TreasureData::NotFoundError
55
+ end
56
+ end
57
+
58
+ def setup_server(ssl_version)
59
+ logger = Logger.new(STDERR)
60
+ logger.level = Logger::Severity::FATAL # avoid logging SSLError (ERROR level)
61
+ @server = WEBrick::HTTPServer.new(
62
+ :BindAddress => "localhost",
63
+ :Logger => logger,
64
+ :Port => 0,
65
+ :AccessLog => [],
66
+ :DocumentRoot => '.',
67
+ :SSLEnable => true,
68
+ :SSLCACertificateFile => File.join(DIR, 'ca.cert'),
69
+ :SSLCertificate => cert('server.cert'),
70
+ :SSLPrivateKey => key('server.key')
71
+ )
72
+ @serverport = @server.config[:Port]
73
+ @server.mount(
74
+ '/hello',
75
+ WEBrick::HTTPServlet::ProcHandler.new(method(:do_hello).to_proc)
76
+ )
77
+ @server.ssl_context.ssl_version = ssl_version
78
+ @server_thread = start_server_thread(@server)
79
+ return @server
80
+ end
81
+
82
+ def do_hello(req, res)
83
+ res['content-type'] = 'text/html'
84
+ res.body = "hello"
85
+ end
86
+
87
+ def start_server_thread(server)
88
+ t = Thread.new {
89
+ Thread.current.abort_on_exception = true
90
+ server.start
91
+ }
92
+ while server.status != :Running
93
+ sleep 0.1
94
+ unless t.alive?
95
+ t.join
96
+ raise
97
+ end
98
+ end
99
+ t
100
+ end
101
+
102
+ def cert(filename)
103
+ OpenSSL::X509::Certificate.new(File.read(File.join(DIR, filename)))
104
+ end
105
+
106
+ def key(filename)
107
+ OpenSSL::PKey::RSA.new(File.read(File.join(DIR, filename)))
108
+ end
109
+ end
@@ -0,0 +1,199 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+ require 'td/client/spec_resources'
5
+ require 'tempfile'
6
+
7
+ describe 'BulkImport API' do
8
+ include_context 'spec symbols'
9
+ include_context 'common helper'
10
+
11
+ let :api do
12
+ API.new(nil)
13
+ end
14
+
15
+ let :packed do
16
+ s = StringIO.new
17
+ Zlib::GzipWriter.wrap(s) do |f|
18
+ pk = MessagePack::Packer.new(f)
19
+ pk.write([1, '2', 3.0])
20
+ pk.write([4, '5', 6.0])
21
+ pk.write([7, '8', 9.0])
22
+ pk.flush
23
+ end
24
+ s.string
25
+ end
26
+
27
+ let(:endpoint_domain) { TreasureData::API::DEFAULT_IMPORT_ENDPOINT }
28
+
29
+ describe 'create_bulk_import' do
30
+ it 'should create a new bulk_import' do
31
+ stub_api_request(:post, "/v3/bulk_import/create/#{e(bi_name)}/#{e(db_name)}/#{e(table_name)}").
32
+ to_return(:body => {'bulk_import' => bi_name}.to_json)
33
+
34
+ expect(api.create_bulk_import(bi_name, db_name, table_name)).to be_nil
35
+ end
36
+
37
+ it 'should return 422 error with invalid name' do
38
+ name = 'D'
39
+ err_msg = "Validation failed: Name is too short" # " (minimum is 3 characters)"
40
+ stub_api_request(:post, "/v3/bulk_import/create/#{e(name)}/#{e(db_name)}/#{e(table_name)}").
41
+ to_return(:status => 422, :body => {'message' => err_msg}.to_json)
42
+
43
+ expect {
44
+ api.create_bulk_import(name, db_name, table_name)
45
+ }.to raise_error(TreasureData::APIError, /#{err_msg}/)
46
+ end
47
+
48
+ it 'should return 404 error with non exist database name' do
49
+ db = 'no_such_db'
50
+ err_msg = "Couldn't find UserDatabase with name = #{db}"
51
+ stub_api_request(:post, "/v3/bulk_import/create/#{e(bi_name)}/#{e(db)}/#{e(table_name)}").
52
+ to_return(:status => 404, :body => {'message' => err_msg}.to_json)
53
+
54
+ expect {
55
+ api.create_bulk_import(bi_name, db, table_name)
56
+ }.to raise_error(TreasureData::APIError, /#{err_msg}/)
57
+ end
58
+
59
+ it 'should return 404 error with non exist table name' do
60
+ table = 'no_such_table'
61
+ err_msg = "Couldn't find UserTableReference with name = #{table}"
62
+ stub_api_request(:post, "/v3/bulk_import/create/#{e(bi_name)}/#{e(db_name)}/#{e(table)}").
63
+ to_return(:status => 404, :body => {'message' => err_msg}.to_json)
64
+
65
+ expect {
66
+ api.create_bulk_import(bi_name, db_name, table)
67
+ }.to raise_error(TreasureData::APIError, /#{err_msg}/)
68
+ end
69
+ end
70
+
71
+ describe 'delete_bulk_import' do
72
+ it 'runs' do
73
+ stub_api_request(:post, '/v3/bulk_import/delete/name').
74
+ with(:body => 'foo=bar')
75
+ expect(api.delete_bulk_import('name', 'foo' => 'bar')).to eq(nil)
76
+ end
77
+ end
78
+
79
+ describe 'show_bulk_import' do
80
+ it 'runs' do
81
+ stub_api_request(:get, '/v3/bulk_import/show/name').
82
+ to_return(:body => {'status' => 'status', 'other' => 'other'}.to_json)
83
+ expect(api.show_bulk_import('name')['status']).to eq('status')
84
+ end
85
+ end
86
+
87
+ describe 'list_bulk_imports' do
88
+ it 'runs' do
89
+ stub_api_request(:get, '/v3/bulk_import/list').
90
+ with(:query => 'foo=bar').
91
+ to_return(:body => {'bulk_imports' => %w(1 2 3)}.to_json)
92
+ expect(api.list_bulk_imports('foo' => 'bar')).to eq(%w(1 2 3))
93
+ end
94
+ end
95
+
96
+ describe 'list_bulk_import_parts' do
97
+ it 'runs' do
98
+ stub_api_request(:get, '/v3/bulk_import/list_parts/name').
99
+ with(:query => 'foo=bar').
100
+ to_return(:body => {'parts' => %w(1 2 3)}.to_json)
101
+ expect(api.list_bulk_import_parts('name', 'foo' => 'bar')).to eq(%w(1 2 3))
102
+ end
103
+ end
104
+
105
+ describe 'bulk_import_upload_part' do
106
+ it 'runs' do
107
+ t = Tempfile.new('bulk_import_spec')
108
+ File.open(t.path, 'w') do |f|
109
+ f << '12345'
110
+ end
111
+ stub_request(:put, "https://#{TreasureData::API::DEFAULT_ENDPOINT}/v3/bulk_import/upload_part/name/part").
112
+ with(:body => '12345')
113
+ File.open(t.path) do |f|
114
+ expect(api.bulk_import_upload_part('name', 'part', f, 5)).to eq(nil)
115
+ end
116
+ end
117
+
118
+ if ''.respond_to?(:encode)
119
+ it 'encodes part_name in UTF-8' do
120
+ t = Tempfile.new('bulk_import_spec')
121
+ File.open(t.path, 'w') do |f|
122
+ f << '12345'
123
+ end
124
+ stub_request(:put, "https://#{TreasureData::API::DEFAULT_ENDPOINT}/v3/bulk_import/upload_part/name/" + CGI.escape('日本語(Japanese)'.encode('UTF-8'))).
125
+ with(:body => '12345')
126
+ File.open(t.path) do |f|
127
+ expect(api.bulk_import_upload_part('name', '日本語(Japanese)'.encode('Windows-31J'), f, 5)).to eq(nil)
128
+ end
129
+ end
130
+ end
131
+ end
132
+
133
+ describe 'bulk_import_delete_part' do
134
+ it 'runs' do
135
+ stub_api_request(:post, '/v3/bulk_import/delete_part/name/part')
136
+ expect(api.bulk_import_delete_part('name', 'part')).to eq(nil)
137
+ end
138
+ end
139
+
140
+ describe 'freeze_bulk_import' do
141
+ it 'runs' do
142
+ stub_api_request(:post, '/v3/bulk_import/freeze/name')
143
+ expect(api.freeze_bulk_import('name')).to eq(nil)
144
+ end
145
+ end
146
+
147
+ describe 'unfreeze_bulk_import' do
148
+ it 'runs' do
149
+ stub_api_request(:post, '/v3/bulk_import/unfreeze/name')
150
+ expect(api.unfreeze_bulk_import('name')).to eq(nil)
151
+ end
152
+ end
153
+
154
+ describe 'perform_bulk_import' do
155
+ it 'runs' do
156
+ stub_api_request(:post, '/v3/bulk_import/perform/name').
157
+ to_return(:body => {'job_id' => 12345}.to_json)
158
+ expect(api.perform_bulk_import('name')).to eq('12345')
159
+ end
160
+ end
161
+
162
+ describe 'commit_bulk_import' do
163
+ it 'runs' do
164
+ stub_api_request(:post, '/v3/bulk_import/commit/name').
165
+ to_return(:body => {'job_id' => 12345}.to_json)
166
+ expect(api.commit_bulk_import('name')).to eq(nil)
167
+ end
168
+ end
169
+
170
+ describe 'bulk_import_error_records' do
171
+ it 'returns [] on empty' do
172
+ stub_api_request(:get, '/v3/bulk_import/error_records/name').
173
+ to_return(:body => '')
174
+ expect(api.bulk_import_error_records('name')).to eq([])
175
+ end
176
+
177
+ it 'returns nil on empty if block given' do
178
+ stub_api_request(:get, '/v3/bulk_import/error_records/name').
179
+ to_return(:body => '')
180
+ expect(api.bulk_import_error_records('name'){}).to eq(nil)
181
+ end
182
+
183
+ it 'returns unpacked result' do
184
+ stub_api_request(:get, '/v3/bulk_import/error_records/name').
185
+ to_return(:body => packed)
186
+ expect(api.bulk_import_error_records('name')).to eq([[1, '2', 3.0], [4, '5', 6.0], [7, '8', 9.0]])
187
+ end
188
+
189
+ it 'yields unpacked result if block given' do
190
+ stub_api_request(:get, '/v3/bulk_import/error_records/name').
191
+ to_return(:body => packed)
192
+ result = []
193
+ api.bulk_import_error_records('name') do |row|
194
+ result << row
195
+ end
196
+ expect(result).to eq([[1, '2', 3.0], [4, '5', 6.0], [7, '8', 9.0]])
197
+ end
198
+ end
199
+ end
@@ -0,0 +1,401 @@
1
+ require 'spec_helper'
2
+ require 'td/client/spec_resources'
3
+ require 'tempfile'
4
+
5
+ describe 'BulkImport API' do
6
+ include_context 'spec symbols'
7
+ include_context 'common helper'
8
+
9
+ let :api do
10
+ Client.new(nil, {:max_cumul_retry_delay => -1})
11
+ end
12
+
13
+ let :retry_api do
14
+ API.new(nil, {:retry_delay => 1, :max_cumul_retry_delay => 1})
15
+ end
16
+
17
+ let :original_config do
18
+ {
19
+ :config => {
20
+ :type => "s3_file",
21
+ :access_key_id => "key",
22
+ :secret_access_key => "secret",
23
+ :endpoint => "s3.amazonaws.com",
24
+ :bucket => "td-bulk-loader-test-tokyo",
25
+ :path_prefix => "in/nahi/sample"
26
+ }
27
+ }
28
+ end
29
+
30
+ let :guessed_config do
31
+ {
32
+ "config" => {
33
+ "type" => "s3_file",
34
+ "access_key_id" => "key",
35
+ "secret_access_key" => "secret",
36
+ "endpoint" => "s3.amazonaws.com",
37
+ "bucket" => "td-bulk-loader-test-tokyo",
38
+ "path_prefix" => "in/nahi/sample",
39
+ "parser" => {
40
+ "charset" => "UTF-8",
41
+ "newline" => "LF",
42
+ "type" => "csv",
43
+ "delimiter" => ",",
44
+ "header_line" => false,
45
+ "columns" => [
46
+ {"name" => "time", "type" => "long"},
47
+ {"name" => "c1", "type" => "long"},
48
+ {"name" => "c2", "type" => "string"},
49
+ {"name" => "c3", "type" => "string"},
50
+ ]
51
+ },
52
+ "decoders" => [
53
+ {"type" => "gzip"}
54
+ ]
55
+ }
56
+ }
57
+ end
58
+
59
+ let :preview_result do
60
+ {
61
+ "schema" => [
62
+ {"index" => 0, "name" => "c0", "type" => "string"},
63
+ {"index" => 1, "name" => "c1", "type" => "long"},
64
+ {"index" => 2, "name" => "c2", "type" => "string"},
65
+ {"index" => 3, "name" => "c3", "type" => "string"}
66
+ ],
67
+ "records" => [
68
+ ["19920116", 32864, "06612", "00195"],
69
+ ["19910729", 14824, "07706", "00058"],
70
+ ["19950708", 27559, "03244", "00034"],
71
+ ["19931010", 11270, "03459", "00159"],
72
+ ["19981117", 20461, "01409", "00128"],
73
+ ["19981117", 20461, "00203", "00128"],
74
+ ["19930108", 44402, "01489", "00001"],
75
+ ["19960104", 16528, "04848", "00184"],
76
+ ["19960104", 16528, "01766", "00184"],
77
+ ["19881022", 26114, "06960", "00175"]
78
+ ]
79
+ }
80
+ end
81
+
82
+ let :bulk_load_session do
83
+ guessed_config.dup.merge(
84
+ {
85
+ "name" => "nahi_test_1",
86
+ "cron" => "@daily",
87
+ "timezone" => "Asia/Tokyo",
88
+ "delay" => 3600
89
+ }
90
+ )
91
+ end
92
+
93
+ let :bulk_load_job do
94
+ guessed_config.dup.merge(
95
+ {
96
+ "job_id" => 123456,
97
+ "account_id" => 1,
98
+ "status" => "success",
99
+ "records" => 10,
100
+ "schema" => [["c0", "string", ""], ["c1", "long", ""], ["c2", "string", ""], ["c3", "string", ""]],
101
+ "database" => {"id" => 189263, "name" => "nahidb"},
102
+ "table" => {"id" => 176281, "name" => "bulkload_import_test"},
103
+ "created_at" => 1426738133,
104
+ "updated_at" => 1426738145,
105
+ "start_at" => 1426738134,
106
+ "end_at" => 1426738144
107
+ }
108
+ )
109
+ end
110
+
111
+ describe 'guess' do
112
+ it 'returns guessed json' do
113
+ stub_api_request(:post, '/v3/bulk_loads/guess').
114
+ with(:body => original_config.to_json).
115
+ to_return(:body => guessed_config.to_json)
116
+ expect(api.bulk_load_guess(
117
+ original_config
118
+ )).to eq(guessed_config)
119
+ end
120
+
121
+ it 'raises an error' do
122
+ stub_api_request(:post, '/v3/bulk_loads/guess').
123
+ with(:body => original_config.to_json).
124
+ to_return(:status => 500, :body => guessed_config.to_json)
125
+ expect {
126
+ api.bulk_load_guess(
127
+ original_config
128
+ )
129
+ }.to raise_error(TreasureData::APIError)
130
+ end
131
+
132
+ it 'perform redo on 500 error' do
133
+ stub_api_request(:post, '/v3/bulk_loads/guess').
134
+ with(:body => original_config.to_json).
135
+ to_return(:status => 500, :body => guessed_config.to_json)
136
+ begin
137
+ expect(retry_api.bulk_load_guess(
138
+ original_config
139
+ )).to != nil
140
+ rescue TreasureData::APIError => e
141
+ expect(e.message).to match(/^500: BulkLoad configuration guess failed/)
142
+ end
143
+ end
144
+
145
+ it 'perform retries on connection failure' do
146
+ api = retry_api
147
+ allow(api.instance_eval { @api }).to receive(:post).and_raise(SocketError.new('>>'))
148
+ begin
149
+ retry_api.bulk_load_guess(
150
+ original_config
151
+ )
152
+ rescue SocketError => e
153
+ expect(e.message).to eq('>> (Retried 1 times in 1 seconds)')
154
+ end
155
+ end
156
+ end
157
+
158
+ describe 'guess with old format' do
159
+ it 'returns guessed json' do
160
+ stub_api_request(:post, '/v3/bulk_loads/guess').
161
+ with(:body => original_config.to_json).
162
+ to_return(:body => guessed_config.to_json)
163
+ expect(api.bulk_load_guess(
164
+ original_config
165
+ )).to eq(guessed_config)
166
+ end
167
+
168
+ it 'raises an error' do
169
+ stub_api_request(:post, '/v3/bulk_loads/guess').
170
+ with(:body => original_config.to_json).
171
+ to_return(:status => 500, :body => guessed_config.to_json)
172
+ expect {
173
+ api.bulk_load_guess(
174
+ original_config
175
+ )
176
+ }.to raise_error(TreasureData::APIError)
177
+ end
178
+
179
+ it 'perform redo on 500 error' do
180
+ stub_api_request(:post, '/v3/bulk_loads/guess').
181
+ with(:body => original_config.to_json).
182
+ to_return(:status => 500, :body => guessed_config.to_json)
183
+ begin
184
+ expect(retry_api.bulk_load_guess(
185
+ original_config
186
+ )).to != nil
187
+ rescue TreasureData::APIError => e
188
+ expect(e.message).to match(/^500: BulkLoad configuration guess failed/)
189
+ end
190
+ end
191
+
192
+ it 'perform retries on connection failure' do
193
+ api = retry_api
194
+ allow(api.instance_eval { @api }).to receive(:post).and_raise(SocketError.new('>>'))
195
+ begin
196
+ retry_api.bulk_load_guess(
197
+ original_config
198
+ )
199
+ rescue SocketError => e
200
+ expect(e.message).to eq('>> (Retried 1 times in 1 seconds)')
201
+ end
202
+ end
203
+ end
204
+
205
+ describe 'preview' do
206
+ it 'returns preview json' do
207
+ stub_api_request(:post, '/v3/bulk_loads/preview').
208
+ with(:body => guessed_config.to_json).
209
+ to_return(:body => preview_result.to_json)
210
+ expect(api.bulk_load_preview(
211
+ guessed_config
212
+ )).to eq(preview_result)
213
+ end
214
+
215
+ it 'raises an error' do
216
+ stub_api_request(:post, '/v3/bulk_loads/preview').
217
+ with(:body => guessed_config.to_json).
218
+ to_return(:status => 500, :body => preview_result.to_json)
219
+ expect {
220
+ api.bulk_load_preview(
221
+ guessed_config
222
+ )
223
+ }.to raise_error(TreasureData::APIError)
224
+ end
225
+ end
226
+
227
+ describe 'issue' do
228
+ it 'returns job id' do
229
+ expected_request = guessed_config.dup
230
+ expected_request['database'] = 'database'
231
+ expected_request['table'] = 'table'
232
+ stub_api_request(:post, '/v3/job/issue/bulkload/database').
233
+ with(:body => expected_request.to_json).
234
+ to_return(:body => {'job_id' => 12345}.to_json)
235
+ expect(api.bulk_load_issue(
236
+ 'database',
237
+ 'table',
238
+ guessed_config
239
+ )).to eq('12345')
240
+ end
241
+ end
242
+
243
+ describe 'list' do
244
+ it 'returns BulkLoadSession' do
245
+ stub_api_request(:get, '/v3/bulk_loads').
246
+ to_return(:body => [bulk_load_session, bulk_load_session].to_json)
247
+ result = api.bulk_load_list
248
+ expect(result.size).to eq(2)
249
+ expect(result.first).to eq(bulk_load_session)
250
+ end
251
+
252
+ it 'returns empty' do
253
+ stub_api_request(:get, '/v3/bulk_loads').
254
+ to_return(:body => [].to_json)
255
+ expect(api.bulk_load_list.size).to eq(0)
256
+ end
257
+ end
258
+
259
+ describe 'create' do
260
+ it 'returns registered bulk_load_session' do
261
+ expected_request = guessed_config.dup
262
+ expected_request['name'] = 'nahi_test_1'
263
+ expected_request['cron'] = '@daily'
264
+ expected_request['timezone'] = 'Asia/Tokyo'
265
+ expected_request['delay'] = 3600
266
+ expected_request['database'] = 'database'
267
+ expected_request['table'] = 'table'
268
+ stub_api_request(:post, '/v3/bulk_loads').
269
+ with(:body => expected_request.to_json).
270
+ to_return(:body => bulk_load_session.to_json)
271
+ expect(api.bulk_load_create(
272
+ 'nahi_test_1',
273
+ 'database',
274
+ 'table',
275
+ guessed_config,
276
+ {
277
+ cron: '@daily',
278
+ timezone: 'Asia/Tokyo',
279
+ delay: 3600
280
+ }
281
+ )).to eq(bulk_load_session)
282
+ end
283
+
284
+ it 'accepts empty option' do
285
+ expected_request = guessed_config.dup
286
+ expected_request['name'] = 'nahi_test_1'
287
+ expected_request['database'] = 'database'
288
+ expected_request['table'] = 'table'
289
+ stub_api_request(:post, '/v3/bulk_loads').
290
+ with(:body => expected_request.to_json).
291
+ to_return(:body => bulk_load_session.to_json)
292
+ expect(api.bulk_load_create(
293
+ 'nahi_test_1',
294
+ 'database',
295
+ 'table',
296
+ guessed_config
297
+ )).to eq(bulk_load_session)
298
+ end
299
+
300
+ it 'accepts time_column option' do
301
+ expected_request = guessed_config.dup
302
+ expected_request['name'] = 'nahi_test_1'
303
+ expected_request['time_column'] = 'c0'
304
+ expected_request['database'] = 'database'
305
+ expected_request['table'] = 'table'
306
+ stub_api_request(:post, '/v3/bulk_loads').
307
+ with(:body => expected_request.to_json).
308
+ to_return(:body => bulk_load_session.to_json)
309
+ expect(api.bulk_load_create(
310
+ 'nahi_test_1',
311
+ 'database',
312
+ 'table',
313
+ guessed_config,
314
+ {
315
+ time_column: 'c0'
316
+ }
317
+ )).to eq(bulk_load_session)
318
+ end
319
+ end
320
+
321
+ describe 'show' do
322
+ it 'returns bulk_load_session' do
323
+ stub_api_request(:get, '/v3/bulk_loads/nahi_test_1').
324
+ to_return(:body => bulk_load_session.to_json)
325
+ expect(api.bulk_load_show('nahi_test_1')).to eq(bulk_load_session)
326
+ end
327
+ end
328
+
329
+ describe 'update' do
330
+ it 'returns updated bulk_load_session' do
331
+ stub_api_request(:put, '/v3/bulk_loads/nahi_test_1').
332
+ with(:body => bulk_load_session.to_json).
333
+ to_return(:body => bulk_load_session.to_json)
334
+ expect(api.bulk_load_update(
335
+ 'nahi_test_1',
336
+ bulk_load_session
337
+ )).to eq(bulk_load_session)
338
+ end
339
+
340
+ it 'returns updated bulk_load_session' do
341
+ updated_bulk_load_session = bulk_load_session.merge({'timezone' => 'America/Los Angeles'})
342
+ stub_api_request(:put, '/v3/bulk_loads/nahi_test_1').
343
+ with(:body => updated_bulk_load_session.to_json).
344
+ to_return(:body => updated_bulk_load_session.to_json)
345
+ expect(api.bulk_load_update(
346
+ 'nahi_test_1',
347
+ updated_bulk_load_session
348
+ )).to eq updated_bulk_load_session
349
+ end
350
+
351
+ it 'can remove the cron schedule ' do
352
+ updated_bulk_load_session = bulk_load_session.merge({'cron' => ''})
353
+ # NOTE: currently the API just ignores an empty 'cron' specification update
354
+ # I am assuming that once fixed, the API will return a nil for cron if unscheduled.
355
+ expected_bulk_load_session = (bulk_load_session['cron'] = nil)
356
+ stub_api_request(:put, '/v3/bulk_loads/nahi_test_1').
357
+ with(:body => updated_bulk_load_session.to_json).
358
+ to_return(:body => expected_bulk_load_session.to_json)
359
+ expect(api.bulk_load_update(
360
+ 'nahi_test_1',
361
+ updated_bulk_load_session
362
+ )).to eq expected_bulk_load_session
363
+ end
364
+ end
365
+
366
+ describe 'delete' do
367
+ it 'returns updated bulk_load_session' do
368
+ stub_api_request(:delete, '/v3/bulk_loads/nahi_test_1').
369
+ to_return(:body => bulk_load_session.to_json)
370
+ expect(api.bulk_load_delete('nahi_test_1')).to eq(bulk_load_session)
371
+ end
372
+ end
373
+
374
+ describe 'history' do
375
+ it 'returns list of jobs' do
376
+ stub_api_request(:get, '/v3/bulk_loads/nahi_test_1/jobs').
377
+ to_return(:body => [bulk_load_job, bulk_load_job].to_json)
378
+ result = api.bulk_load_history('nahi_test_1')
379
+ expect(result.size).to eq(2)
380
+ expect(result.first).to eq(bulk_load_job)
381
+ end
382
+ end
383
+
384
+ describe 'run' do
385
+ it 'returns job_id' do
386
+ stub_api_request(:post, '/v3/bulk_loads/nahi_test_1/jobs').
387
+ with(:body => '{}').
388
+ to_return(:body => {'job_id' => 12345}.to_json)
389
+ expect(api.bulk_load_run('nahi_test_1')).to eq('12345')
390
+ end
391
+
392
+ it 'accepts scheduled_time' do
393
+ now = Time.now.to_i
394
+ stub_api_request(:post, '/v3/bulk_loads/nahi_test_1/jobs').
395
+ with(:body => {scheduled_time: now.to_s}.to_json).
396
+ to_return(:body => {'job_id' => 12345}.to_json)
397
+ expect(api.bulk_load_run('nahi_test_1', now)).to eq('12345')
398
+ end
399
+ end
400
+
401
+ end