mode-sdk 0.0.1
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 +7 -0
- data/.gitignore +17 -0
- data/.yardopts +1 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +109 -0
- data/Rakefile +6 -0
- data/lib/mode/sdk.rb +70 -0
- data/lib/mode/sdk/client.rb +87 -0
- data/lib/mode/sdk/client/request.rb +133 -0
- data/lib/mode/sdk/client/response.rb +83 -0
- data/lib/mode/sdk/column.rb +95 -0
- data/lib/mode/sdk/column_set.rb +56 -0
- data/lib/mode/sdk/configuration.rb +21 -0
- data/lib/mode/sdk/hash_util.rb +21 -0
- data/lib/mode/sdk/table.rb +168 -0
- data/lib/mode/sdk/table_import.rb +52 -0
- data/lib/mode/sdk/upload.rb +57 -0
- data/lib/mode/sdk/version.rb +9 -0
- data/lib/mode/sdk/warehouse_util.rb +35 -0
- data/mode-sdk.gemspec +27 -0
- data/spec/lib/mode/sdk/client/request_spec.rb +105 -0
- data/spec/lib/mode/sdk/client/response_spec.rb +65 -0
- data/spec/lib/mode/sdk/client_spec.rb +71 -0
- data/spec/lib/mode/sdk/column_set_spec.rb +53 -0
- data/spec/lib/mode/sdk/column_spec.rb +101 -0
- data/spec/lib/mode/sdk/hash_util_spec.rb +18 -0
- data/spec/lib/mode/sdk/table_import_spec.rb +33 -0
- data/spec/lib/mode/sdk/table_spec.rb +191 -0
- data/spec/lib/mode/sdk/upload_spec.rb +37 -0
- data/spec/lib/mode/sdk/warehouse_util_spec.rb +40 -0
- data/spec/lib/mode/sdk_spec.rb +50 -0
- data/spec/spec_helper.rb +17 -0
- metadata +145 -0
@@ -0,0 +1,101 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Mode::Sdk::Column do
|
6
|
+
describe '#validate!' do
|
7
|
+
it 'returns true if no exceptions are raised' do
|
8
|
+
column = Mode::Sdk::Column.new({})
|
9
|
+
|
10
|
+
expect(column).to receive(:validate_keys!)
|
11
|
+
expect(column).to receive(:validate_name!)
|
12
|
+
expect(column).to receive(:validate_type!)
|
13
|
+
|
14
|
+
expect(column.validate!).to eq(true)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe '#validate_keys!' do
|
19
|
+
it 'raises error if column is missing name key' do
|
20
|
+
column = Mode::Sdk::Column.new(type: 'text')
|
21
|
+
|
22
|
+
expect {
|
23
|
+
column.send(:validate_keys!)
|
24
|
+
}.to raise_error(Mode::Sdk::Column::InvalidError) do |exception|
|
25
|
+
expect(exception.message).to match(/missing required key 'name'/i)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'raises error if column is missing type key' do
|
30
|
+
column = Mode::Sdk::Column.new(name: 'name')
|
31
|
+
|
32
|
+
expect {
|
33
|
+
column.send(:validate_keys!)
|
34
|
+
}.to raise_error(Mode::Sdk::Column::InvalidError) do |exception|
|
35
|
+
expect(exception.message).to match(/missing required key 'type'/i)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'raises error if column has extra keys' do
|
40
|
+
column = Mode::Sdk::Column.new(name: 'name', type: 'text', foo: 'bar')
|
41
|
+
|
42
|
+
expect {
|
43
|
+
column.send(:validate_keys!)
|
44
|
+
}.to raise_error(Mode::Sdk::Column::InvalidError) do |exception|
|
45
|
+
expect(exception.message).to match(/has unexpected keys/i)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'returns true for valid keys' do
|
50
|
+
column = Mode::Sdk::Column.new(name: nil, type: nil)
|
51
|
+
|
52
|
+
expect(column.send(:validate_keys!)).to eq(true)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
describe '#validate_name!' do
|
57
|
+
it 'raises error if column name is too short' do
|
58
|
+
column = Mode::Sdk::Column.new(name: 'n')
|
59
|
+
|
60
|
+
expect {
|
61
|
+
column.send(:validate_name!)
|
62
|
+
}.to raise_error(Mode::Sdk::Column::InvalidError) do |exception|
|
63
|
+
expect(exception.message).to match(/name is invalid/i)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'raises error if column name is too long' do
|
68
|
+
column = Mode::Sdk::Column.new(name: 'no' * 50)
|
69
|
+
|
70
|
+
expect {
|
71
|
+
column.send(:validate_name!)
|
72
|
+
}.to raise_error(Mode::Sdk::Column::InvalidError) do |exception|
|
73
|
+
expect(exception.message).to match(/name is invalid/i)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'returns true for valid name' do
|
78
|
+
column = Mode::Sdk::Column.new(name: 'name')
|
79
|
+
|
80
|
+
expect(column.send(:validate_name!)).to eq(true)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
describe '#validate_type!' do
|
85
|
+
it 'raises error if column type is not recognized' do
|
86
|
+
column = Mode::Sdk::Column.new(type: 'nope')
|
87
|
+
|
88
|
+
expect {
|
89
|
+
column.send(:validate_type!)
|
90
|
+
}.to raise_error(Mode::Sdk::Column::InvalidError) do |exception|
|
91
|
+
expect(exception.message).to match(/type is invalid/i)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'returns true for valid type' do
|
96
|
+
column = Mode::Sdk::Column.new(type: 'integer')
|
97
|
+
|
98
|
+
expect(column.send(:validate_type!)).to eq(true)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Mode::Sdk::HashUtil do
|
6
|
+
let :util do
|
7
|
+
Object.new.extend(Mode::Sdk::HashUtil)
|
8
|
+
end
|
9
|
+
|
10
|
+
describe '#stringify_keys' do
|
11
|
+
it 'stringifies keys' do
|
12
|
+
hash = { foo1: 'bar1', foo2: 'bar2' }
|
13
|
+
|
14
|
+
expect(util.stringify_keys(hash)).to eq(
|
15
|
+
'foo1' => 'bar1', 'foo2' => 'bar2')
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Mode::Sdk::TableImport do
|
6
|
+
describe '#poll' do
|
7
|
+
it 'polls state until completed' do
|
8
|
+
import = Mode::Sdk::TableImport.new('/some/path')
|
9
|
+
|
10
|
+
reprs = %w(new enqueued running running failed).map do |state|
|
11
|
+
{ 'state' => state }
|
12
|
+
end
|
13
|
+
|
14
|
+
expect(import).to receive(:fetch_repr).and_return(*reprs)
|
15
|
+
|
16
|
+
expect { |b|
|
17
|
+
import.poll(0, &b)
|
18
|
+
}.to yield_successive_args(*reprs)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe '#fetch_repr' do
|
23
|
+
it 'performs get request' do
|
24
|
+
import = Mode::Sdk::TableImport.new('/some/path')
|
25
|
+
|
26
|
+
response = double(:response, body: { 'state' => 'enqueued' })
|
27
|
+
|
28
|
+
expect(Mode::Sdk::Client).to receive(:get).and_return(response)
|
29
|
+
|
30
|
+
expect(import.send(:fetch_repr)).to eq('state' => 'enqueued')
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,191 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Mode::Sdk::Table do
|
6
|
+
describe '#owner' do
|
7
|
+
it 'defaults to authenticated username' do
|
8
|
+
expect(Mode::Sdk).to receive(:username).and_return('sally')
|
9
|
+
table = Mode::Sdk::Table.new('name')
|
10
|
+
expect(table.send(:owner)).to eq('sally')
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'uses owner option if present' do
|
14
|
+
expect(Mode::Sdk).to receive(:username).never
|
15
|
+
table = Mode::Sdk::Table.new('name', owner: 'earl')
|
16
|
+
expect(table.send(:owner)).to eq('earl')
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe '#resource_path' do
|
21
|
+
it 'forms path correctly' do
|
22
|
+
table = Mode::Sdk::Table.new('name', owner: 'sally')
|
23
|
+
expect(table.send(:resource_path)).to eq('/api/sally/tables/name')
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe '#exists?' do
|
28
|
+
it 'is true if table exists' do
|
29
|
+
response = Mode::Sdk::Client::Response.new(nil)
|
30
|
+
expect(response).to receive(:code).and_return(200)
|
31
|
+
expect(Mode::Sdk::Client).to receive(:head).and_return(response)
|
32
|
+
|
33
|
+
table = Mode::Sdk::Table.new('name', owner: 'sally')
|
34
|
+
expect(table.send(:exists?)).to eq(true)
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'is false if table does not exist' do
|
38
|
+
response = Mode::Sdk::Client::Response.new(nil)
|
39
|
+
expect(response).to receive(:code).and_return(404)
|
40
|
+
expect(Mode::Sdk::Client).to receive(:head).and_return(response)
|
41
|
+
|
42
|
+
table = Mode::Sdk::Table.new('name', owner: 'sally')
|
43
|
+
expect(table.send(:exists?)).to eq(false)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe '#create' do
|
48
|
+
it 'creates table' do
|
49
|
+
table = Mode::Sdk::Table.new('name')
|
50
|
+
|
51
|
+
expect(table).to receive(:upsert) do |*args|
|
52
|
+
expect(args.first).to eq(:create)
|
53
|
+
end
|
54
|
+
|
55
|
+
expect(table.create).to eq(true)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe '#replace' do
|
60
|
+
it 'creates table' do
|
61
|
+
table = Mode::Sdk::Table.new('name')
|
62
|
+
|
63
|
+
expect(table).to receive(:upsert) do |*args|
|
64
|
+
expect(args.first).to eq(:replace)
|
65
|
+
end
|
66
|
+
|
67
|
+
expect(table.replace).to eq(true)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe '#validate!' do
|
72
|
+
it 'raises exception if upload token is missing' do
|
73
|
+
table = Mode::Sdk::Table.new('no')
|
74
|
+
|
75
|
+
expect {
|
76
|
+
table.send(:validate!)
|
77
|
+
}.to raise_error(Mode::Sdk::Table::InvalidError) do |exception|
|
78
|
+
expect(exception.message).to match(/missing required attribute/i)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'raises exception if table name is invalid' do
|
83
|
+
table = Mode::Sdk::Table.new('no')
|
84
|
+
table.upload_token = 'token'
|
85
|
+
|
86
|
+
expect {
|
87
|
+
table.send(:validate!)
|
88
|
+
}.to raise_error(Mode::Sdk::Table::InvalidError) do |exception|
|
89
|
+
expect(exception.message).to match(/invalid name/i)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'returns true if no exceptions are raised' do
|
94
|
+
table = Mode::Sdk::Table.new('name')
|
95
|
+
table.upload_token = 'token'
|
96
|
+
|
97
|
+
expect_any_instance_of(Mode::Sdk::ColumnSet).to(
|
98
|
+
receive(:validate!).at_least(:once).and_return(true))
|
99
|
+
|
100
|
+
expect(table.send(:validate!)).to eq(true)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
describe '#upsert' do
|
105
|
+
let :response do
|
106
|
+
Mode::Sdk::Client::Response.new(nil)
|
107
|
+
end
|
108
|
+
|
109
|
+
describe 'for create' do
|
110
|
+
it 'creates table' do
|
111
|
+
table = Mode::Sdk::Table.new('name', owner: 'owner')
|
112
|
+
|
113
|
+
expect(table).to receive(:validate!)
|
114
|
+
expect(response).to receive(:code).and_return(200)
|
115
|
+
expect(Mode::Sdk::Client).to receive(:post).and_return(response)
|
116
|
+
|
117
|
+
expect(table.send(:upsert, :create)).to be_an_instance_of(
|
118
|
+
Mode::Sdk::Client::Response)
|
119
|
+
end
|
120
|
+
|
121
|
+
it 'raises exception for bad request' do
|
122
|
+
table = Mode::Sdk::Table.new('name', owner: 'owner')
|
123
|
+
|
124
|
+
expect(table).to receive(:validate!)
|
125
|
+
expect(response).to receive(:code).and_return(400)
|
126
|
+
expect(response).to receive(:body)
|
127
|
+
expect(Mode::Sdk::Client).to receive(:post).and_return(response)
|
128
|
+
|
129
|
+
expect {
|
130
|
+
table.send(:upsert, :create)
|
131
|
+
}.to raise_error(Mode::Sdk::Table::InvalidError)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
describe 'for replace' do
|
136
|
+
it 'replaces table' do
|
137
|
+
table = Mode::Sdk::Table.new('name', owner: 'owner')
|
138
|
+
|
139
|
+
expect(table).to receive(:validate!)
|
140
|
+
expect(response).to receive(:code).and_return(200)
|
141
|
+
expect(Mode::Sdk::Client).to receive(:put).and_return(response)
|
142
|
+
|
143
|
+
expect(table.send(:upsert, :replace)).to be_an_instance_of(
|
144
|
+
Mode::Sdk::Client::Response)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
describe '#full_name' do
|
150
|
+
it 'forms full name with schema' do
|
151
|
+
table = Mode::Sdk::Table.new('name', owner: 'owner')
|
152
|
+
|
153
|
+
expect(table.full_name).to eq('owner.name')
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
describe '#upsert_data' do
|
158
|
+
it 'is formed correctly' do
|
159
|
+
table = Mode::Sdk::Table.new('name', owner: 'owner')
|
160
|
+
table.columns = [{ name: 'name', type: 'string' }]
|
161
|
+
table.upload_token = 'token'
|
162
|
+
|
163
|
+
result = table.send(:upsert_data)
|
164
|
+
|
165
|
+
expected = {
|
166
|
+
upload_token: 'token',
|
167
|
+
table: {
|
168
|
+
name: 'name',
|
169
|
+
description: nil,
|
170
|
+
columns: [{ 'name' => 'name', 'type' => 'string' }]
|
171
|
+
}
|
172
|
+
}
|
173
|
+
|
174
|
+
expect(result).to eq(expected)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
describe '#columns=' do
|
179
|
+
it 'unmemoizes column set' do
|
180
|
+
table = Mode::Sdk::Table.new('name', owner: 'owner')
|
181
|
+
table.columns = [{ name: 'name', type: 'string' }]
|
182
|
+
table.send(:column_set)
|
183
|
+
|
184
|
+
expect(table.instance_variable_defined?(:@column_set)).to eq(true)
|
185
|
+
|
186
|
+
table.columns = [{ name: 'name', type: 'string' }]
|
187
|
+
|
188
|
+
expect(table.instance_variable_defined?(:@column_set)).to eq(false)
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Mode::Sdk::Upload do
|
6
|
+
describe '#create' do
|
7
|
+
it 'performs http post' do
|
8
|
+
upload = Mode::Sdk::Upload.new('content')
|
9
|
+
|
10
|
+
expect(Mode::Sdk::Client).to receive(:post) do |*args|
|
11
|
+
expect(args[0]).to eq('/api/uploads')
|
12
|
+
|
13
|
+
expect(args[1]).to eq('content')
|
14
|
+
|
15
|
+
options = args[2]
|
16
|
+
|
17
|
+
expect(options[:content_type]).to eq('application/csv')
|
18
|
+
expect(options[:content_length]).to eq(7)
|
19
|
+
expect(options[:expect]).to eq([201])
|
20
|
+
end
|
21
|
+
|
22
|
+
upload.create
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe '#token' do
|
27
|
+
it 'retrieves the token' do
|
28
|
+
upload = Mode::Sdk::Upload.new('content')
|
29
|
+
|
30
|
+
response = Mode::Sdk::Client::Response.new(nil)
|
31
|
+
expect(response).to receive(:body).and_return('token' => 'sometoken')
|
32
|
+
expect(upload).to receive(:create).and_return(response)
|
33
|
+
|
34
|
+
expect(upload.token).to eq('sometoken')
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Mode::Sdk::WarehouseUtil do
|
6
|
+
let :util do
|
7
|
+
Object.new.extend(Mode::Sdk::WarehouseUtil)
|
8
|
+
end
|
9
|
+
|
10
|
+
describe '#normalize_name' do
|
11
|
+
it 'preserves short name' do
|
12
|
+
expect(util.normalize_name('foo_bar')).to eq('foo_bar')
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'truncates long name' do
|
16
|
+
expect(util.normalize_name('a' * 100).size).to eq(64)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe '#normalize_string' do
|
21
|
+
it 'preserves simple strings' do
|
22
|
+
%w(foo foo_bar foo_bar_baz).each do |string|
|
23
|
+
expect(util.send(:normalize_string, string)).to eq(string)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'normalizes messy strings' do
|
28
|
+
expected = {
|
29
|
+
' fOO_' => 'foo',
|
30
|
+
'_ foo___' => 'foo',
|
31
|
+
'fo*(*o__BaR ! ?' => 'foo_bar',
|
32
|
+
'fo__o- _!bar_b _ - az ' => 'fo_o_bar_b_az'
|
33
|
+
}
|
34
|
+
|
35
|
+
expected.each do |input, output|
|
36
|
+
expect(util.send(:normalize_string, input)).to eq(output)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Mode::Sdk do
|
6
|
+
after :each do
|
7
|
+
Mode::Sdk.reset
|
8
|
+
end
|
9
|
+
|
10
|
+
describe '.configure' do
|
11
|
+
it 'sets configuration variables' do
|
12
|
+
expect_any_instance_of(
|
13
|
+
Mode::Sdk::Configuration).to receive(:setting=).once
|
14
|
+
|
15
|
+
Mode::Sdk.configure do |config|
|
16
|
+
config.setting = 'value'
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe '.config' do
|
22
|
+
it 'instantiates and memoizes configuration instance' do
|
23
|
+
expect(Mode::Sdk.instance_variable_get(:@config)).to be_nil
|
24
|
+
|
25
|
+
expect(Mode::Sdk.config).to be_an_instance_of(Mode::Sdk::Configuration)
|
26
|
+
|
27
|
+
config = Mode::Sdk.instance_variable_get(:@config)
|
28
|
+
|
29
|
+
expect(config).to be_an_instance_of(Mode::Sdk::Configuration)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe '.account' do
|
34
|
+
it 'returns account from client' do
|
35
|
+
expect(Mode::Sdk::Client).to receive(:account).and_return(
|
36
|
+
'username' => 'someone')
|
37
|
+
|
38
|
+
expect(Mode::Sdk.account).to eq('username' => 'someone')
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe '.username' do
|
43
|
+
it 'returns username from client' do
|
44
|
+
expect(Mode::Sdk::Client).to receive(:account).and_return(
|
45
|
+
'username' => 'someone')
|
46
|
+
|
47
|
+
expect(Mode::Sdk.username).to eq('someone')
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|