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.
- checksums.yaml +7 -0
- data/.gitignore +52 -0
- data/.ruby-gemset +1 -0
- data/.travis.yml +16 -0
- data/Gemfile +22 -0
- data/LICENSE +21 -0
- data/Rakefile +37 -0
- data/authentication.md +104 -0
- data/examples/add_delete_users.rb +94 -0
- data/examples/change_credentials_email_address_for_users.rb +23 -0
- data/examples/create_credentials_email_for_users.rb +19 -0
- data/examples/delete_all_user_sessions.rb +15 -0
- data/examples/delete_credentials_google_for_users.rb +19 -0
- data/examples/generate_password_reset_tokens_for_users.rb +19 -0
- data/examples/ldap_roles_test.rb +50 -0
- data/examples/me.rb +3 -0
- data/examples/refresh_user_notification_addresses.rb +10 -0
- data/examples/roles_and_users_with_permission.rb +22 -0
- data/examples/sdk_setup.rb +21 -0
- data/examples/streaming_downloads.rb +20 -0
- data/examples/users_with_credentials_email.rb +6 -0
- data/examples/users_with_credentials_google.rb +8 -0
- data/examples/users_with_credentials_google_without_credentials_email.rb +6 -0
- data/lib/looker-sdk.rb +32 -0
- data/lib/looker-sdk/authentication.rb +104 -0
- data/lib/looker-sdk/client.rb +445 -0
- data/lib/looker-sdk/client/dynamic.rb +107 -0
- data/lib/looker-sdk/configurable.rb +116 -0
- data/lib/looker-sdk/default.rb +148 -0
- data/lib/looker-sdk/error.rb +235 -0
- data/lib/looker-sdk/rate_limit.rb +33 -0
- data/lib/looker-sdk/response/raise_error.rb +20 -0
- data/lib/looker-sdk/sawyer_patch.rb +33 -0
- data/lib/looker-sdk/version.rb +7 -0
- data/looker-sdk.gemspec +27 -0
- data/readme.md +117 -0
- data/shell/.gitignore +41 -0
- data/shell/Gemfile +6 -0
- data/shell/readme.md +18 -0
- data/shell/shell.rb +37 -0
- data/streaming.md +59 -0
- data/test/helper.rb +46 -0
- data/test/looker/swagger.json +1998 -0
- data/test/looker/test_client.rb +258 -0
- data/test/looker/test_dynamic_client.rb +158 -0
- data/test/looker/test_dynamic_client_agent.rb +131 -0
- data/test/looker/user.json +1 -0
- metadata +107 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: a9c372139d8df28af2b71bce4382f3badfa6d1c8
|
4
|
+
data.tar.gz: 66910d788e066ef3767f76966e1973ca7312e3c4
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 043052d8df89d53724b1fe23e181ae5eafb598ff4aca2dd44d8d030510ffe98dffeb8c51a9a180c594b64597500d42b13db84f08d1969fcdb3d571c98e9eb49e
|
7
|
+
data.tar.gz: 8627156eb5061245b16330094381786800e1c32c1fbb8340297e962429a97ad14cc75e2fc2fe8073297e4c7b61751298c629da8ee65721c38c0b5975b643f644
|
data/.gitignore
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
# Numerous always-ignore extensions
|
2
|
+
*.diff
|
3
|
+
*.err
|
4
|
+
*.orig
|
5
|
+
*.log
|
6
|
+
*.rej
|
7
|
+
*.swo
|
8
|
+
*.swp
|
9
|
+
*.vi
|
10
|
+
*~
|
11
|
+
*.sass-cache
|
12
|
+
*.iml
|
13
|
+
|
14
|
+
# OS or Editor folders
|
15
|
+
.DS_Store
|
16
|
+
.cache
|
17
|
+
.project
|
18
|
+
.settings
|
19
|
+
.tmproj
|
20
|
+
nbproject
|
21
|
+
Thumbs.db
|
22
|
+
|
23
|
+
# Folders to ignore
|
24
|
+
intermediate
|
25
|
+
publish
|
26
|
+
target
|
27
|
+
.idea
|
28
|
+
out
|
29
|
+
|
30
|
+
# markdown previews
|
31
|
+
*.md.html
|
32
|
+
|
33
|
+
# bundler suggested ignores
|
34
|
+
*.gem
|
35
|
+
*.rbc
|
36
|
+
.bundle
|
37
|
+
.config
|
38
|
+
.yardoc
|
39
|
+
Gemfile.lock
|
40
|
+
Gemfile.optional.lock
|
41
|
+
InstalledFiles
|
42
|
+
_yardoc
|
43
|
+
coverage
|
44
|
+
doc/
|
45
|
+
lib/bundler/man
|
46
|
+
pkg
|
47
|
+
rdoc
|
48
|
+
spec/reports
|
49
|
+
test/tmp
|
50
|
+
test/version_tmp
|
51
|
+
tmp
|
52
|
+
*.netrc
|
data/.ruby-gemset
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
looker-sdk-ruby
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
group :development do
|
4
|
+
gem 'awesome_print', '~>1.6.1', :require => 'ap'
|
5
|
+
gem 'redcarpet', '~>3.1.2', :platforms => :ruby
|
6
|
+
end
|
7
|
+
|
8
|
+
group :development, :test do
|
9
|
+
gem 'rake', '< 11.0'
|
10
|
+
end
|
11
|
+
|
12
|
+
group :test do
|
13
|
+
# gem 'json', '~> 1.7', :platforms => [:jruby] look TODO needed?
|
14
|
+
gem 'minitest', '5.3.5'
|
15
|
+
gem 'mocha', '1.1.0'
|
16
|
+
gem 'rack', '1.6.4'
|
17
|
+
gem 'rack-test', '0.6.2'
|
18
|
+
gem 'netrc', '~> 0.7.7'
|
19
|
+
gem 'simplecov', '~> 0.7.1', :require => false
|
20
|
+
end
|
21
|
+
|
22
|
+
gemspec
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2015 Looker Data Sciences, Inc.
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
require "bundler/gem_tasks"
|
3
|
+
|
4
|
+
require "rake/testtask"
|
5
|
+
Rake::TestTask.new do |t|
|
6
|
+
t.libs << "test"
|
7
|
+
t.test_files = FileList["test/**/test_*.rb"]
|
8
|
+
t.verbose = true
|
9
|
+
end
|
10
|
+
|
11
|
+
namespace :test do
|
12
|
+
desc "Run tests against all supported Rubies"
|
13
|
+
task :all do
|
14
|
+
supported_rubies = ['ruby-1.9.3', 'ruby-2.0', 'ruby-2.1', 'ruby-2.3.1', 'jruby-1.7.19', 'jruby-9.1.5.0']
|
15
|
+
failing_rubies = []
|
16
|
+
|
17
|
+
supported_rubies.each do |ruby|
|
18
|
+
cmd = "rvm install #{ruby} && rvm #{ruby} exec gem install bundler && rvm #{ruby} exec bundle install && rvm #{ruby} exec bundle exec rake"
|
19
|
+
system cmd
|
20
|
+
if $? != 0
|
21
|
+
failing_rubies << ruby
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
failing_rubies.each do |ruby|
|
26
|
+
puts "FAIL: #{ruby}. Problem with the tests on #{ruby}."
|
27
|
+
end
|
28
|
+
|
29
|
+
if failing_rubies
|
30
|
+
exit 1
|
31
|
+
else
|
32
|
+
exit 0
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
task :default => :test
|
data/authentication.md
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
## How to authenticate to Looker's API 3
|
2
|
+
|
3
|
+
The preferred way to authenticate is to use the looker SDK to manage the login and the passing of
|
4
|
+
access_tokens as needed. This doc, however, explains how to do this authentication in a generic way without the SDK and using curl instead for illustration.
|
5
|
+
|
6
|
+
Looker API 3 implements OAuth 2's "Resource Owner Password Credentials Grant" pattern,
|
7
|
+
See: http://tools.ietf.org/html/rfc6749#section-4.3
|
8
|
+
|
9
|
+
### Setup an API key
|
10
|
+
An 'API 3' key is required in order to login and use the API. The key consists of a client_id and a client_secret.
|
11
|
+
'Login' consists of using these credentials to generate a short-term access_token which is then used to make API calls.
|
12
|
+
|
13
|
+
The client_id could be considered semi-public. While, the client_secret is a secret password and MUST be
|
14
|
+
carefully protected. These credentials should not be hard-coded into client code. They should be read from
|
15
|
+
a closely guarded data file when used by client processes.
|
16
|
+
|
17
|
+
Admins can create an API 3 key for a user on looker's user edit page. All requests made using these
|
18
|
+
credentials are made 'as' that user and limited to the role permissions specified for that user. A user
|
19
|
+
account with an appropriate role may be created as needed for the API client's use.
|
20
|
+
|
21
|
+
Note that API 3 tokens should be created for 'regular' Looker users and *not* via the legacy 'Add API User' button.
|
22
|
+
|
23
|
+
|
24
|
+
### Ensure that the API is accessible
|
25
|
+
Looker versions 3.4 (and beyond) expose the 3.0 API via a port different from the port used by the web app.
|
26
|
+
The default port is 19999. It may be necessary to have the Ops team managing the looker instance ensure that this
|
27
|
+
port is made accessible network-wise to client software running on non-local hosts.
|
28
|
+
|
29
|
+
The '/alive' url can be used to detect if the server is reachable
|
30
|
+
|
31
|
+
|
32
|
+
### Login
|
33
|
+
To access the API it is necessary to 'login' using the client_id and client_secret in order to acquire an
|
34
|
+
access_token that will be used in actual API requests. This is done by POSTing to the /login url. The access_token is returned in a short json body and has a limited time before it expires (the default at this point is 1 hour).
|
35
|
+
An 'expires_in' field is provided to tell client software how long they should expect the token to last.
|
36
|
+
|
37
|
+
A new token is created for each /login call and remains valid until it expires or is revoked via /logout.
|
38
|
+
|
39
|
+
It is VERY important that these tokens never be sent in the clear or exposed in any other way.
|
40
|
+
|
41
|
+
|
42
|
+
### Call the API
|
43
|
+
API calls then pass the access_token to looker using an 'Authorization' header. API calls are
|
44
|
+
done using GET, PUT, POST, PATCH, or DELETE as appropriate for the specific call. Normal REST stuff.
|
45
|
+
|
46
|
+
|
47
|
+
### Logout
|
48
|
+
A '/logout' url is available if the client wants to revoke an access_token. It requires a DELETE request.
|
49
|
+
Looker reserves the right to limit the number of 'live' access_tokens per user.
|
50
|
+
|
51
|
+
-------------------------------------------------------------------------------------------------
|
52
|
+
|
53
|
+
The following is an example session using Curl. The '-i' param is used to show returned headers.
|
54
|
+
|
55
|
+
The simple flow in this example is to login, get info about the current user, then logout.
|
56
|
+
|
57
|
+
Note that in this example the client_id and client_secret params are passed using '-d' which causes the
|
58
|
+
request to be done as a POST. And, the -H param is used to specify http headers to add for API requests.
|
59
|
+
|
60
|
+
```
|
61
|
+
# Check that the port is reachable
|
62
|
+
> curl -i https://localhost:19999/alive
|
63
|
+
HTTP/1.1 200 OK
|
64
|
+
Content-Type: application/json;charset=utf-8
|
65
|
+
Vary: Accept-Encoding
|
66
|
+
X-Content-Type-Options: nosniff
|
67
|
+
Content-Length: 0
|
68
|
+
|
69
|
+
|
70
|
+
# Do the login to get an access_token
|
71
|
+
> curl -i -d "client_id=4j3SD8W5RchHw5gvZ5Yd&client_secret=sVySctSMpQQG3TzdNQ5d2dND" https://localhost:19999/login
|
72
|
+
HTTP/1.1 200 OK
|
73
|
+
Content-Type: application/json;charset=utf-8
|
74
|
+
Vary: Accept-Encoding
|
75
|
+
X-Content-Type-Options: nosniff
|
76
|
+
Content-Length: 99
|
77
|
+
|
78
|
+
{"access_token":"4QDkCyCtZzYgj4C2p2cj3csJH7zqS5RzKs2kTnG4","token_type":"Bearer","expires_in":3600}
|
79
|
+
|
80
|
+
# Use an access_token (the token can be used over and over for API calls until it expires)
|
81
|
+
> curl -i -H "Authorization: token 4QDkCyCtZzYgj4C2p2cj3csJH7zqS5RzKs2kTnG4" https://localhost:19999/api/3.0/user
|
82
|
+
HTTP/1.1 200 OK
|
83
|
+
Content-Type: application/json;charset=utf-8
|
84
|
+
Vary: Accept-Encoding
|
85
|
+
X-Content-Type-Options: nosniff
|
86
|
+
Content-Length: 502
|
87
|
+
|
88
|
+
{"id":14,"first_name":"Plain","last_name":"User","email":"dude+1@looker.com","models_dir":null,"is_disabled":false,"look_access":[14],"avatar_url":"https://www.gravatar.com/avatar/b7f792a6180a36a4058f36875584bc45?s=156&d=mm","credentials_email":{"email":"dude+1@looker.com","url":"https://localhost:19999/api/3.0/users/14/credentials_email","user_url":"https://localhost:19999/api/3.0/users/14","password_reset_url":"https://localhost:19999/api/3.0"},"url":"https://localhost:19999/api/3.0/users/14"}
|
89
|
+
|
90
|
+
# Logout to revoke an access_token
|
91
|
+
> curl -i -X DELETE -H "Authorization: token 4QDkCyCtZzYgj4C2p2cj3csJH7zqS5RzKs2kTnG4" https://localhost:19999/logout
|
92
|
+
HTTP/1.1 204 No Content
|
93
|
+
X-Content-Type-Options: nosniff
|
94
|
+
|
95
|
+
# Show that the access_token is no longer valid
|
96
|
+
> curl -i -X DELETE -H "Authorization: token 4QDkCyCtZzYgj4C2p2cj3csJH7zqS5RzKs2kTnG4" https://localhost:19999/logout
|
97
|
+
HTTP/1.1 404 Not Found
|
98
|
+
Content-Type: application/json;charset=utf-8
|
99
|
+
Vary: Accept-Encoding
|
100
|
+
X-Content-Type-Options: nosniff
|
101
|
+
Content-Length: 69
|
102
|
+
|
103
|
+
{"message":"Not found","documentation_url":"http://docs.looker.com/"}
|
104
|
+
```
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require './sdk_setup'
|
2
|
+
|
3
|
+
############################################################################################
|
4
|
+
# simulate a list read from file
|
5
|
+
|
6
|
+
user_list = <<-ENDMARK
|
7
|
+
|
8
|
+
Joe Williams, joe@mycoolcompany.com, Admin
|
9
|
+
Jane Schumacher, jane@mycoolcompany.com, User SuperDeveloper
|
10
|
+
Jim Watson, jim@mycoolcompany.com, User
|
11
|
+
Jim Wu, jimw@mycoolcompany.com, User
|
12
|
+
|
13
|
+
ENDMARK
|
14
|
+
|
15
|
+
############################################################################################
|
16
|
+
|
17
|
+
def create_users(lines)
|
18
|
+
# create a hash to use below. role.name => role.id
|
19
|
+
roles_by_name = Hash[ sdk.all_roles.map{|role| [role.name, role.id] } ]
|
20
|
+
|
21
|
+
# for each line, try to create that user with name, email credentials, and roles
|
22
|
+
lines.each_line do |line|
|
23
|
+
line.strip!
|
24
|
+
next if line.empty?
|
25
|
+
|
26
|
+
# quicky parsing: note lack of error handling!
|
27
|
+
|
28
|
+
name, email, roles = line.split(',')
|
29
|
+
fname, lname = name.split(' ')
|
30
|
+
[fname, lname, email, roles].each(&:strip!)
|
31
|
+
|
32
|
+
role_ids = []
|
33
|
+
roles.split(' ').each do |role_name|
|
34
|
+
if id = roles_by_name[role_name]
|
35
|
+
role_ids << id
|
36
|
+
else
|
37
|
+
raise "#{role_name} does not exist. ABORTING!"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# for display
|
42
|
+
user_info = "#{fname} #{lname} <#{email}> as #{roles}"
|
43
|
+
|
44
|
+
begin
|
45
|
+
# call the SDK to create user with names, add email/password login credentials, set user roles
|
46
|
+
user = sdk.create_user({:first_name => fname, :last_name => lname})
|
47
|
+
sdk.create_user_credentials_email(user.id, {:email => email})
|
48
|
+
sdk.set_user_roles(user.id, role_ids)
|
49
|
+
puts "Created user: #{user_info}"
|
50
|
+
rescue LookerSDK::Error => e
|
51
|
+
# if any errors occur above then 'undo' by deleting the given user (if we got that far)
|
52
|
+
sdk.delete_user(user.id) if user
|
53
|
+
puts "FAILED to create user: #{user_info} (#{e.message}) "
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
##################################################################
|
59
|
+
|
60
|
+
def delete_users(lines)
|
61
|
+
# create a hash to use below. user.credentials_email.email => user.id
|
62
|
+
users_by_email = Hash[ sdk.all_users.map{|user| [user.credentials_email.email, user.id] if user.credentials_email}.compact ]
|
63
|
+
|
64
|
+
lines.each_line do |line|
|
65
|
+
line.strip!
|
66
|
+
next if line.empty?
|
67
|
+
|
68
|
+
# quicky parsing: note lack of error handling!
|
69
|
+
name, email, roles = line.split(',')
|
70
|
+
fname, lname = name.split(' ')
|
71
|
+
[fname, lname, email, roles].each(&:strip!)
|
72
|
+
|
73
|
+
# for display
|
74
|
+
user_info = "#{fname} #{lname} <#{email}>"
|
75
|
+
|
76
|
+
begin
|
77
|
+
if id = users_by_email[email]
|
78
|
+
sdk.delete_user(id)
|
79
|
+
puts "Deleted user: #{user_info}>"
|
80
|
+
else
|
81
|
+
puts "Did not find user: #{user_info}>"
|
82
|
+
end
|
83
|
+
rescue LookerSDK::Error => e
|
84
|
+
puts "FAILED to delete user: #{user_info} (#{e.message}) "
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
##################################################################
|
90
|
+
|
91
|
+
# call the methods
|
92
|
+
create_users(user_list)
|
93
|
+
puts
|
94
|
+
delete_users(user_list)
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require './sdk_setup'
|
2
|
+
|
3
|
+
users = Hash[
|
4
|
+
sdk.all_users(:fields => 'id, credentials_email').
|
5
|
+
map{|u| [u.credentials_email.email, u.id] if u.credentials_email }.compact
|
6
|
+
]
|
7
|
+
|
8
|
+
$stdin.each_line do |line|
|
9
|
+
line.chomp!
|
10
|
+
|
11
|
+
_, old_email, new_email = line.split(',', 3).map(&:strip)
|
12
|
+
|
13
|
+
if id = users[old_email]
|
14
|
+
begin
|
15
|
+
sdk.update_user_credentials_email(id, {:email => new_email})
|
16
|
+
puts "Successfully changed '#{old_email}' => '#{new_email}'"
|
17
|
+
rescue => e
|
18
|
+
puts "FAILED to changed '#{old_email}' => '#{new_email}' because of: #{e.class}:#{e.message}"
|
19
|
+
end
|
20
|
+
else
|
21
|
+
puts "FAILED: Could not find user with email '#{old_email}'"
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require './sdk_setup'
|
2
|
+
|
3
|
+
$stdin.each_line do |line|
|
4
|
+
line.chomp!
|
5
|
+
|
6
|
+
id, email = line.split(',', 2).map(&:strip)
|
7
|
+
|
8
|
+
begin
|
9
|
+
user = sdk.user(id)
|
10
|
+
if user.credentials_email
|
11
|
+
puts "Error: User with id '#{id}' Already has credentials_email"
|
12
|
+
else
|
13
|
+
sdk.create_user_credentials_email(id, {:email => email})
|
14
|
+
puts "Success: Created credentials_email for User with id '#{id}' and email '#{email}'"
|
15
|
+
end
|
16
|
+
rescue LookerSDK::NotFound
|
17
|
+
puts "Error: User with id '#{id}' Not found"
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require './sdk_setup'
|
2
|
+
|
3
|
+
total_count = 0
|
4
|
+
sdk.all_users(:fields => 'id, display_name').each do |user|
|
5
|
+
count = 0
|
6
|
+
sdk.all_user_sessions(user.id, :fields => 'id').each do |session|
|
7
|
+
sdk.delete_user_session(user.id, session.id)
|
8
|
+
count += 1
|
9
|
+
end
|
10
|
+
puts "Deleted #{count} sessions for #{user.id} #{user.display_name}"
|
11
|
+
total_count += count
|
12
|
+
end
|
13
|
+
puts "Deleted #{total_count} sessions"
|
14
|
+
|
15
|
+
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require './sdk_setup'
|
2
|
+
|
3
|
+
$stdin.each_line do |line|
|
4
|
+
line.chomp!
|
5
|
+
|
6
|
+
id, _ = line.split(',', 2).map(&:strip)
|
7
|
+
|
8
|
+
begin
|
9
|
+
user = sdk.user(id)
|
10
|
+
if user.credentials_google
|
11
|
+
sdk.delete_user_credentials_google(id)
|
12
|
+
puts "Success: Deleted credentials_google for User with id '#{id}'"
|
13
|
+
else
|
14
|
+
puts "Error: User with id '#{id}' Does not have credentials_google"
|
15
|
+
end
|
16
|
+
rescue LookerSDK::NotFound
|
17
|
+
puts "Error: User with id '#{id}' Not found"
|
18
|
+
end
|
19
|
+
end
|