looker-sdk 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +52 -0
  3. data/.ruby-gemset +1 -0
  4. data/.travis.yml +16 -0
  5. data/Gemfile +22 -0
  6. data/LICENSE +21 -0
  7. data/Rakefile +37 -0
  8. data/authentication.md +104 -0
  9. data/examples/add_delete_users.rb +94 -0
  10. data/examples/change_credentials_email_address_for_users.rb +23 -0
  11. data/examples/create_credentials_email_for_users.rb +19 -0
  12. data/examples/delete_all_user_sessions.rb +15 -0
  13. data/examples/delete_credentials_google_for_users.rb +19 -0
  14. data/examples/generate_password_reset_tokens_for_users.rb +19 -0
  15. data/examples/ldap_roles_test.rb +50 -0
  16. data/examples/me.rb +3 -0
  17. data/examples/refresh_user_notification_addresses.rb +10 -0
  18. data/examples/roles_and_users_with_permission.rb +22 -0
  19. data/examples/sdk_setup.rb +21 -0
  20. data/examples/streaming_downloads.rb +20 -0
  21. data/examples/users_with_credentials_email.rb +6 -0
  22. data/examples/users_with_credentials_google.rb +8 -0
  23. data/examples/users_with_credentials_google_without_credentials_email.rb +6 -0
  24. data/lib/looker-sdk.rb +32 -0
  25. data/lib/looker-sdk/authentication.rb +104 -0
  26. data/lib/looker-sdk/client.rb +445 -0
  27. data/lib/looker-sdk/client/dynamic.rb +107 -0
  28. data/lib/looker-sdk/configurable.rb +116 -0
  29. data/lib/looker-sdk/default.rb +148 -0
  30. data/lib/looker-sdk/error.rb +235 -0
  31. data/lib/looker-sdk/rate_limit.rb +33 -0
  32. data/lib/looker-sdk/response/raise_error.rb +20 -0
  33. data/lib/looker-sdk/sawyer_patch.rb +33 -0
  34. data/lib/looker-sdk/version.rb +7 -0
  35. data/looker-sdk.gemspec +27 -0
  36. data/readme.md +117 -0
  37. data/shell/.gitignore +41 -0
  38. data/shell/Gemfile +6 -0
  39. data/shell/readme.md +18 -0
  40. data/shell/shell.rb +37 -0
  41. data/streaming.md +59 -0
  42. data/test/helper.rb +46 -0
  43. data/test/looker/swagger.json +1998 -0
  44. data/test/looker/test_client.rb +258 -0
  45. data/test/looker/test_dynamic_client.rb +158 -0
  46. data/test/looker/test_dynamic_client_agent.rb +131 -0
  47. data/test/looker/user.json +1 -0
  48. metadata +107 -0
@@ -0,0 +1,258 @@
1
+ require_relative '../helper'
2
+
3
+ describe LookerSDK::Client do
4
+
5
+ before(:each) do
6
+ setup_sdk
7
+ end
8
+
9
+ after(:each) do
10
+ teardown_sdk
11
+ end
12
+
13
+ describe "module configuration" do
14
+
15
+ before do
16
+ LookerSDK.reset!
17
+ LookerSDK.configure do |config|
18
+ LookerSDK::Configurable.keys.each do |key|
19
+ config.send("#{key}=", "Some #{key}")
20
+ end
21
+ end
22
+ end
23
+
24
+ after do
25
+ LookerSDK.reset!
26
+ end
27
+
28
+ it "inherits the module configuration" do
29
+ client = LookerSDK::Client.new
30
+ LookerSDK::Configurable.keys.each do |key|
31
+ client.instance_variable_get(:"@#{key}").must_equal("Some #{key}")
32
+ end
33
+ end
34
+
35
+ describe "with class level configuration" do
36
+
37
+ before do
38
+ @opts = {
39
+ :connection_options => {:ssl => {:verify => false}},
40
+ :per_page => 40,
41
+ :client_id => "looker_client_id",
42
+ :client_secret => "client_secret2"
43
+ }
44
+ end
45
+
46
+ it "overrides module configuration" do
47
+ client = LookerSDK::Client.new(@opts)
48
+ client.per_page.must_equal(40)
49
+ client.client_id.must_equal("looker_client_id")
50
+ client.instance_variable_get(:"@client_secret").must_equal("client_secret2")
51
+ client.auto_paginate.must_equal(LookerSDK.auto_paginate)
52
+ client.client_id.wont_equal(LookerSDK.client_id)
53
+ end
54
+
55
+ it "can set configuration after initialization" do
56
+ client = LookerSDK::Client.new
57
+ client.configure do |config|
58
+ @opts.each do |key, value|
59
+ config.send("#{key}=", value)
60
+ end
61
+ end
62
+ client.per_page.must_equal(40)
63
+ client.client_id.must_equal("looker_client_id")
64
+ client.instance_variable_get(:"@client_secret").must_equal("client_secret2")
65
+ client.auto_paginate.must_equal(LookerSDK.auto_paginate)
66
+ client.client_id.wont_equal(LookerSDK.client_id)
67
+ end
68
+
69
+ it "masks client_secrets on inspect" do
70
+ client = LookerSDK::Client.new(@opts)
71
+ inspected = client.inspect
72
+ inspected.wont_include("client_secret2")
73
+ end
74
+
75
+ it "masks tokens on inspect" do
76
+ client = LookerSDK::Client.new(:access_token => '87614b09dd141c22800f96f11737ade5226d7ba8')
77
+ inspected = client.inspect
78
+ inspected.wont_equal("87614b09dd141c22800f96f11737ade5226d7ba8")
79
+ end
80
+
81
+ it "masks client secrets on inspect" do
82
+ client = LookerSDK::Client.new(:client_secret => '87614b09dd141c22800f96f11737ade5226d7ba8')
83
+ inspected = client.inspect
84
+ inspected.wont_equal("87614b09dd141c22800f96f11737ade5226d7ba8")
85
+ end
86
+
87
+ describe "with .netrc" do
88
+ it "can read .netrc files" do
89
+ LookerSDK.reset!
90
+ client = LookerSDK::Client.new(:netrc => true, :netrc_file => File.join(fixture_path, '.netrc'))
91
+ client.client_id.wont_be_nil
92
+ client.client_secret.wont_be_nil
93
+ end
94
+ end
95
+ end
96
+
97
+ describe "config tests" do
98
+
99
+ before do
100
+ LookerSDK.reset!
101
+ end
102
+
103
+ it "sets oauth token with .configure" do
104
+ client = LookerSDK::Client.new
105
+ client.configure do |config|
106
+ config.access_token = 'd255197b4937b385eb63d1f4677e3ffee61fbaea'
107
+ end
108
+ client.application_credentials?.must_equal false
109
+ client.token_authenticated?.must_equal true
110
+ end
111
+
112
+ it "sets oauth token with initializer block" do
113
+ client = LookerSDK::Client.new do |config|
114
+ config.access_token = 'd255197b4937b385eb63d1f4677e3ffee61fbaea'
115
+ end
116
+ client.application_credentials?.must_equal false
117
+ client.token_authenticated?.must_equal true
118
+ end
119
+
120
+ it "sets oauth token with instance methods" do
121
+ client = LookerSDK::Client.new
122
+ client.access_token = 'd255197b4937b385eb63d1f4677e3ffee61fbaea'
123
+ client.application_credentials?.must_equal false
124
+ client.token_authenticated?.must_equal true
125
+ end
126
+
127
+ it "sets oauth application creds with .configure" do
128
+ client = LookerSDK::Client.new
129
+ client.configure do |config|
130
+ config.client_id = '97b4937b385eb63d1f46'
131
+ config.client_secret = 'd255197b4937b385eb63d1f4677e3ffee61fbaea'
132
+ end
133
+ client.application_credentials?.must_equal true
134
+ client.token_authenticated?.must_equal false
135
+ end
136
+
137
+ it "sets oauth application creds with initializer block" do
138
+ client = LookerSDK::Client.new do |config|
139
+ config.client_id = '97b4937b385eb63d1f46'
140
+ config.client_secret = 'd255197b4937b385eb63d1f4677e3ffee61fbaea'
141
+ end
142
+ client.application_credentials?.must_equal true
143
+ client.token_authenticated?.must_equal false
144
+ end
145
+
146
+ it "sets oauth token with module methods" do
147
+ client = LookerSDK::Client.new
148
+ client.client_id = '97b4937b385eb63d1f46'
149
+ client.client_secret = 'd255197b4937b385eb63d1f4677e3ffee61fbaea'
150
+ client.application_credentials?.must_equal true
151
+ client.token_authenticated?.must_equal false
152
+ end
153
+ end
154
+
155
+ end
156
+
157
+ describe "request options" do
158
+
159
+ it "parse_query_and_convenience_headers must handle good input" do
160
+
161
+ [
162
+ # no need for empty query or headers
163
+ [{}, {}],
164
+ [{query: {}}, {}],
165
+ [{headers: {}}, {}],
166
+ [{query: {}, headers: {}}, {}],
167
+
168
+ # promote raw stuff into query
169
+ [{query:{foo:'bar'}}, {query:{foo:'bar'}}],
170
+ [{foo:'bar'}, {query:{foo:'bar'}}],
171
+ [{foo:'bar', one:1}, {query:{foo:'bar', one:1}}],
172
+
173
+ # promote CONVENIENCE_HEADERS into headers
174
+ [{accept: 'foo'}, {headers:{accept: 'foo'}}],
175
+ [{content_type: 'foo'}, {headers:{content_type: 'foo'}}],
176
+ [{accept: 'foo', content_type: 'bar'}, {headers:{accept: 'foo', content_type: 'bar'}}],
177
+
178
+ # merge CONVENIENCE_HEADERS into headers if headers not empty
179
+ [{accept: 'foo', headers:{content_type: 'bar'}}, {headers:{accept: 'foo', content_type: 'bar'}}],
180
+
181
+ # promote CONVENIENCE_HEADERS into headers while also handling query parts
182
+ [{accept: 'foo', content_type: 'bar', query:{foo:'bar'}}, {query:{foo:'bar'}, headers:{accept: 'foo', content_type: 'bar'}}],
183
+ [{accept: 'foo', content_type: 'bar', foo:'bar'}, {query:{foo:'bar'}, headers:{accept: 'foo', content_type: 'bar'}}],
184
+
185
+ ].each do |pair|
186
+ input_original, expected = pair
187
+ input = input_original.dup
188
+
189
+ output = LookerSDK::Client.new.send(:parse_query_and_convenience_headers, input)
190
+
191
+ input.must_equal input_original
192
+ output.must_equal expected
193
+ end
194
+
195
+ # don't make the code above handle the special case of nil input.
196
+ LookerSDK::Client.new.send(:parse_query_and_convenience_headers, nil).must_equal({})
197
+ end
198
+
199
+ it "parse_query_and_convenience_headers must detect bad input" do
200
+ [
201
+ 1,
202
+ '',
203
+ [],
204
+ {query:1},
205
+ {query:[]},
206
+ ].each do |input|
207
+ proc { LookerSDK::Client.new.send(:parse_query_and_convenience_headers, input) }.must_raise RuntimeError
208
+ end
209
+ end
210
+
211
+ end
212
+
213
+ describe 'Sawyer date/time parsing patch' do
214
+ describe 'key matches time_field pattern' do
215
+ it 'does not modify non-iso date/time string' do
216
+ values = {
217
+ :test_at => '30 days',
218
+ :test_on => 'July 20, 1969',
219
+ :test_date => '1968-04-03 12:23:34', # this is not iso8601 format!
220
+ :date => '2 months ago',
221
+ }
222
+
223
+ serializer = LookerSDK::Client.new.send(:serializer)
224
+ values.each {|k,v| serializer.decode_hash_value(k,v).must_equal v, k}
225
+ end
226
+
227
+ it 'converts iso date/time strings to Ruby date/time' do
228
+ iso_values = {
229
+ :test_at => '2017-02-07T13:21:50-08:00',
230
+ :test_on => '2017-02-07T00:00:00z',
231
+ :test_date => '1969-07-20T00:00:00-08:00',
232
+ :date => '1968-04-03T12:23:34z',
233
+ }
234
+ serializer = LookerSDK::Client.new.send(:serializer)
235
+ iso_values.each {|k,v| serializer.decode_hash_value(k,v).must_be_kind_of Time, k}
236
+ end
237
+ end
238
+
239
+ describe 'key does NOT match time_field pattern' do
240
+ it 'ignores time-like values' do
241
+ values = {
242
+ :testat => '30 days',
243
+ :teston => '2017-02-07T13:21:50-08:00',
244
+ :testdate => '1968-04-03T12:23:34z',
245
+ :range => '2 months ago for 1 month'
246
+ }
247
+
248
+ serializer = LookerSDK::Client.new.send(:serializer)
249
+ values.each {|k,v| serializer.decode_hash_value(k,v).must_equal v, k}
250
+ end
251
+ end
252
+ end
253
+
254
+ # TODO: Convert the old tests that were here to deal with swagger/dynamic way of doing things. Perhaps
255
+ # with a dedicated server that serves swagger customized to the test suite. Also, bring the auth tests
256
+ # to life here on the SDK client end.
257
+
258
+ end
@@ -0,0 +1,158 @@
1
+ require_relative '../helper'
2
+
3
+ describe LookerSDK::Client::Dynamic do
4
+
5
+ def access_token
6
+ '87614b09dd141c22800f96f11737ade5226d7ba8'
7
+ end
8
+
9
+ def sdk_client(swagger, engine)
10
+ faraday = Faraday.new do |conn|
11
+ conn.use LookerSDK::Response::RaiseError
12
+ conn.adapter :rack, engine
13
+ end
14
+
15
+ LookerSDK::Client.new do |config|
16
+ config.swagger = swagger
17
+ config.access_token = access_token
18
+ config.faraday = faraday
19
+ end
20
+ end
21
+
22
+ def default_swagger
23
+ @swagger ||= JSON.parse(File.read(File.join(File.dirname(__FILE__), 'swagger.json')), :symbolize_names => true)
24
+ end
25
+
26
+ def response
27
+ [200, {'Content-Type' => 'application/json'}, [{}.to_json]]
28
+ end
29
+
30
+ def delete_response
31
+ [204, {}, []]
32
+ end
33
+
34
+ def confirm_env(env, method, path, body, query, content_type)
35
+ req = Rack::Request.new(env)
36
+ req_body = req.body.gets || ''
37
+
38
+ req.base_url.must_equal 'https://localhost:19999'
39
+ req.request_method.must_equal method.to_s.upcase
40
+ req.path_info.must_equal path
41
+
42
+ env["HTTP_AUTHORIZATION"].must_equal "token #{access_token}"
43
+
44
+ JSON.parse(req.params.to_json, :symbolize_names => true).must_equal query
45
+
46
+ begin
47
+ JSON.parse(req_body, :symbolize_names => true).must_equal body
48
+ rescue JSON::ParserError => e
49
+ req_body.must_equal body
50
+ end
51
+
52
+ req.content_type.must_equal(content_type) if content_type
53
+
54
+ # puts env
55
+ # puts req.inspect
56
+ # puts req.params.inspect
57
+ # puts req_body
58
+ # puts req.content_type
59
+
60
+ true
61
+ end
62
+
63
+ def verify(response, method, path, body='', query={}, content_type = nil)
64
+ mock = MiniTest::Mock.new.expect(:call, response){|env| confirm_env(env, method, path, body, query, content_type)}
65
+ yield sdk_client(default_swagger, mock)
66
+ mock.verify
67
+ end
68
+
69
+
70
+ describe "swagger" do
71
+
72
+ it "get" do
73
+ verify(response, :get, '/api/3.0/user') do |sdk|
74
+ sdk.me
75
+ end
76
+ end
77
+
78
+ it "get with parms" do
79
+ verify(response, :get, '/api/3.0/users/25') do |sdk|
80
+ sdk.user(25)
81
+ end
82
+ end
83
+
84
+ it "get with query" do
85
+ verify(response, :get, '/api/3.0/user', '', {bar:"foo"}) do |sdk|
86
+ sdk.me({bar:'foo'})
87
+ end
88
+ end
89
+
90
+ it "get with params and query" do
91
+ verify(response, :get, '/api/3.0/users/25', '', {bar:"foo"}) do |sdk|
92
+ sdk.user(25, {bar:'foo'})
93
+ end
94
+ end
95
+
96
+ it "post" do
97
+ verify(response, :post, '/api/3.0/users', {first_name:'Joe'}) do |sdk|
98
+ sdk.create_user({first_name:'Joe'})
99
+ end
100
+ end
101
+
102
+ it "post with default body" do
103
+ verify(response, :post, '/api/3.0/users', {}) do |sdk|
104
+ sdk.create_user()
105
+ end
106
+ end
107
+
108
+ it "post with default body and default content_type" do
109
+ verify(response, :post, '/api/3.0/users', {}, {}, "application/json") do |sdk|
110
+ sdk.create_user()
111
+ end
112
+ end
113
+
114
+ it "post with default body and specific content_type at option-level" do
115
+ verify(response, :post, '/api/3.0/users', {}, {}, "application/vnd.BOGUS1+json") do |sdk|
116
+ sdk.create_user({}, {:content_type => "application/vnd.BOGUS1+json"})
117
+ end
118
+ end
119
+
120
+ it "post with default body and specific content_type in headers" do
121
+ verify(response, :post, '/api/3.0/users', {}, {}, "application/vnd.BOGUS2+json") do |sdk|
122
+ sdk.create_user({}, {:headers => {:content_type => "application/vnd.BOGUS2+json"}})
123
+ end
124
+ end
125
+
126
+ it "post with file upload" do
127
+ verify(response, :post, '/api/3.0/users', {first_name:'Joe', last_name:'User'}, {}, "application/vnd.BOGUS3+json") do |sdk|
128
+ name = File.join(File.dirname(__FILE__), 'user.json')
129
+ sdk.create_user(Faraday::UploadIO.new(name, "application/vnd.BOGUS3+json"))
130
+ end
131
+ end
132
+
133
+ it "patch" do
134
+ verify(response, :patch, '/api/3.0/users/25', {first_name:'Jim'}) do |sdk|
135
+ sdk.update_user(25, {first_name:'Jim'})
136
+ end
137
+ end
138
+
139
+ it "patch with query" do
140
+ verify(response, :post, '/api/3.0/users', {first_name:'Jim'}, {foo:'bar', baz:'bla'}) do |sdk|
141
+ sdk.create_user({first_name:'Jim'}, {foo:'bar', baz:'bla'})
142
+ end
143
+ end
144
+
145
+ it "put" do
146
+ verify(response, :put, '/api/3.0/users/25/roles', [10, 20]) do |sdk|
147
+ sdk.set_user_roles(25, [10,20])
148
+ end
149
+ end
150
+
151
+ it "delete" do
152
+ verify(delete_response, :delete, '/api/3.0/users/25') do |sdk|
153
+ sdk.delete_user(25)
154
+ end
155
+ end
156
+
157
+ end
158
+ end
@@ -0,0 +1,131 @@
1
+ require_relative '../helper'
2
+
3
+ describe LookerSDK::Client::Dynamic do
4
+
5
+ def access_token
6
+ '87614b09dd141c22800f96f11737ade5226d7ba8'
7
+ end
8
+
9
+ def sdk_client(swagger)
10
+ LookerSDK::Client.new do |config|
11
+ config.swagger = swagger
12
+ config.access_token = access_token
13
+ end
14
+ end
15
+
16
+ def default_swagger
17
+ @swagger ||= JSON.parse(File.read(File.join(File.dirname(__FILE__), 'swagger.json')), :symbolize_names => true)
18
+ end
19
+
20
+ def sdk
21
+ @sdk ||= sdk_client(default_swagger)
22
+ end
23
+
24
+ def with_stub(klass, method, result)
25
+ klass.stubs(method).returns(result)
26
+ begin
27
+ yield
28
+ ensure
29
+ klass.unstub(method)
30
+ end
31
+ end
32
+
33
+ def response
34
+ OpenStruct.new(:data => "foo", :status => 200)
35
+ end
36
+
37
+ def delete_response
38
+ OpenStruct.new(:data => "", :status => 204)
39
+ end
40
+
41
+ describe "swagger" do
42
+ it "get" do
43
+ mock = MiniTest::Mock.new.expect(:call, response, [:get, '/api/3.0/user', nil, {}])
44
+ with_stub(Sawyer::Agent, :new, mock) do
45
+ sdk.me
46
+ mock.verify
47
+ end
48
+ end
49
+
50
+ it "get with parms" do
51
+ mock = MiniTest::Mock.new.expect(:call, response, [:get, '/api/3.0/users/25', nil, {}])
52
+ with_stub(Sawyer::Agent, :new, mock) do
53
+ sdk.user(25)
54
+ mock.verify
55
+ end
56
+ end
57
+
58
+ it "get with query" do
59
+ mock = MiniTest::Mock.new.expect(:call, response, [:get, '/api/3.0/user', nil, {query:{bar:"foo"}}])
60
+ with_stub(Sawyer::Agent, :new, mock) do
61
+ sdk.me({bar:'foo'})
62
+ mock.verify
63
+ end
64
+ end
65
+
66
+ it "get with params and query" do
67
+ mock = MiniTest::Mock.new.expect(:call, response, [:get, '/api/3.0/users/25', nil, {query:{bar:"foo"}}])
68
+ with_stub(Sawyer::Agent, :new, mock) do
69
+ sdk.user(25, {bar:'foo'})
70
+ mock.verify
71
+ end
72
+ end
73
+
74
+ it "post" do
75
+ mock = MiniTest::Mock.new.expect(:call, response, [:post, '/api/3.0/users', {first_name:'Joe'}, {:headers=>{:content_type=>"application/json"}}])
76
+ with_stub(Sawyer::Agent, :new, mock) do
77
+ sdk.create_user({first_name:'Joe'})
78
+ mock.verify
79
+ end
80
+ end
81
+
82
+ it "post with default body" do
83
+ mock = MiniTest::Mock.new.expect(:call, response, [:post, '/api/3.0/users', {}, {:headers=>{:content_type=>"application/json"}}])
84
+ with_stub(Sawyer::Agent, :new, mock) do
85
+ sdk.create_user()
86
+ mock.verify
87
+ end
88
+ end
89
+
90
+ it "patch" do
91
+ mock = MiniTest::Mock.new.expect(:call, response, [:patch, '/api/3.0/users/25', {first_name:'Jim'}, {:headers=>{:content_type=>"application/json"}}])
92
+ with_stub(Sawyer::Agent, :new, mock) do
93
+ sdk.update_user(25, {first_name:'Jim'})
94
+ mock.verify
95
+ end
96
+ end
97
+
98
+ it "put" do
99
+ mock = MiniTest::Mock.new.expect(:call, response, [:put, '/api/3.0/users/25/roles', [10, 20], {:headers=>{:content_type=>"application/json"}}])
100
+ with_stub(Sawyer::Agent, :new, mock) do
101
+ sdk.set_user_roles(25, [10,20])
102
+ mock.verify
103
+ end
104
+ end
105
+
106
+ it "put with nil body" do
107
+ mock = MiniTest::Mock.new.expect(:call, response, [:put, '/api/3.0/users/25/roles', nil, {}])
108
+ with_stub(Sawyer::Agent, :new, mock) do
109
+ sdk.set_user_roles(25, nil)
110
+ mock.verify
111
+ end
112
+ end
113
+
114
+ it "put with empty body" do
115
+ mock = MiniTest::Mock.new.expect(:call, response, [:put, '/api/3.0/users/25/roles', {}, {:headers=>{:content_type=>"application/json"}}])
116
+ with_stub(Sawyer::Agent, :new, mock) do
117
+ sdk.set_user_roles(25, {})
118
+ mock.verify
119
+ end
120
+ end
121
+
122
+ it "delete" do
123
+ mock = MiniTest::Mock.new.expect(:call, delete_response, [:delete, '/api/3.0/users/25', nil, {}])
124
+ with_stub(Sawyer::Agent, :new, mock) do
125
+ sdk.delete_user(25)
126
+ mock.verify
127
+ end
128
+ end
129
+
130
+ end
131
+ end