looker-sdk 0.0.5

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 (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