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,33 @@
1
+ module LookerSDK
2
+
3
+ # Class for API Rate Limit info
4
+ #
5
+ # @!attribute [w] limit
6
+ # @return [Fixnum] Max tries per rate limit period
7
+ # @!attribute [w] remaining
8
+ # @return [Fixnum] Remaining tries per rate limit period
9
+ # @!attribute [w] resets_at
10
+ # @return [Time] Indicates when rate limit resets
11
+ # @!attribute [w] resets_in
12
+ # @return [Fixnum] Number of seconds when rate limit resets
13
+ #
14
+ # @see look TODO docs link
15
+ class RateLimit < Struct.new(:limit, :remaining, :resets_at, :resets_in)
16
+
17
+ # Get rate limit info from HTTP response
18
+ #
19
+ # @param response [#headers] HTTP response
20
+ # @return [RateLimit]
21
+ def self.from_response(response)
22
+ info = new
23
+ if response && !response.headers.nil?
24
+ info.limit = (response.headers['X-RateLimit-Limit'] || 1).to_i
25
+ info.remaining = (response.headers['X-RateLimit-Remaining'] || 1).to_i
26
+ info.resets_at = Time.at((response.headers['X-RateLimit-Reset'] || Time.now).to_i)
27
+ info.resets_in = (info.resets_at - Time.now).to_i
28
+ end
29
+
30
+ info
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,20 @@
1
+ require 'faraday'
2
+ require 'looker-sdk/error'
3
+
4
+ module LookerSDK
5
+ # Faraday response middleware
6
+ module Response
7
+
8
+ # HTTP status codes returned by the API
9
+ class RaiseError < Faraday::Response::Middleware
10
+
11
+ private
12
+
13
+ def on_complete(response)
14
+ if error = LookerSDK::Error.from_response(response)
15
+ raise error
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,33 @@
1
+ # Make Sawyer decode the body lazily.
2
+ # This is a temp monkey-patch until sawyer has: https://github.com/lostisland/sawyer/pull/31
3
+ # At that point we can remove this and update our dependency to the new Sawyer release version.
4
+
5
+ module Sawyer
6
+ class Response
7
+
8
+ attr_reader :env, :body
9
+
10
+ def initialize(agent, res, options = {})
11
+ @agent = agent
12
+ @status = res.status
13
+ @headers = res.headers
14
+ @env = res.env
15
+ @body = res.body
16
+ @rels = process_rels
17
+ @started = options[:sawyer_started]
18
+ @ended = options[:sawyer_ended]
19
+ end
20
+
21
+ def data
22
+ @data ||= begin
23
+ return(body) unless (headers[:content_type] =~ /json|msgpack/)
24
+ process_data(agent.decode_body(body))
25
+ end
26
+ end
27
+
28
+ def inspect
29
+ %(#<#{self.class}: #{@status} @rels=#{@rels.inspect} @data=#{data.inspect}>)
30
+ end
31
+
32
+ end
33
+ end
@@ -0,0 +1,7 @@
1
+ module LookerSDK
2
+
3
+ # Current version
4
+ # @return [String]
5
+ VERSION = "0.0.5"
6
+
7
+ end
@@ -0,0 +1,27 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path('../lib', __FILE__)
3
+ require 'looker-sdk/version'
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = 'looker-sdk'
7
+ s.version = LookerSDK::VERSION
8
+ s.date = "#{Time.now.strftime('%F')}"
9
+ s.authors = ['Looker']
10
+ s.email = 'support@looker.com'
11
+ s.homepage = 'https://github.com/looker/looker-sdk-ruby'
12
+ s.summary = %q{Looker Ruby SDK}
13
+ s.description = 'Use this SDK to access the Looker API. The Looker API provides functions to perform administrative '+
14
+ 'tasks such as provisioning users, configuring database connections, and so on. It also enables you to leverage '+
15
+ 'the Looker data analytics engine to fetch data or render visualizations defined in your Looker data models. '+
16
+ 'For more information, see https://looker.com.'
17
+ s.license = 'MIT'
18
+ s.required_ruby_version = '>= 1.9.3'
19
+ s.requirements = 'Looker version 4.0 or later' # informational
20
+
21
+ s.files = `git ls-files`.split("\n")
22
+ s.test_files = `git ls-files -- {test}/*`.split("\n")
23
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
24
+ s.require_paths = %w(lib)
25
+ s.add_dependency 'jruby-openssl' if s.platform == :jruby
26
+ s.add_dependency 'sawyer', '0.6'
27
+ end
@@ -0,0 +1,117 @@
1
+ # [Looker](http://looker.com/) SDK for Ruby [![Build Status](https://travis-ci.org/looker/looker-sdk-ruby.svg)](https://travis-ci.org/looker/looker-sdk-ruby)
2
+ ### Overview
3
+ This SDK supports secure/authenticated access to the Looker RESTful API. The SDK binds dynamically to the Looker API and builds mappings for the sets of API methods that the Looker instance exposes. This allows for writing straightforward Ruby scripts to interact with the Looker API. And, it allows the SDK to provide access to new Looker API features in each Looker release without requiring an update to the SDK each time.
4
+
5
+ The Looker API uses OAuth2 authentication. 'API3' keys can be generated by Looker admins for any Looker user account from the Looker admin panel. These 'keys' each consist of a client_id/client_secret pair. These keys should be carefully protected as one would with any critical password. When using the SDK, one creates a client object that is initialized with a client_id/client_secret pair and the base URL of the Looker instance's API endpoint. The SDK transparently logs in to the API with that key pair to generate a short-term auth token that it sends to the API with each subsequent call to provide authentication for that call.
6
+
7
+ All calls to the Looker API must be done over a TSL/SSL connection. Requests and responses are then encrypted at that transport layer. It is highly recommended that Looker instance https endpoints use certificates that are properly signed by a trusted certificate authority. The SDK will, by default, validate server certificates. It is possible to disable that validation when creating an SDK client object if necessary. But, that configuration is discouraged.
8
+
9
+ Looker instances expose API documentation at: https://mygreatcompany.looker.com:19999/api-docs/index.html (the exact URL can be set in the Looker admin panel). By default, the documentation page requires a client_id/client_secret pair to load the detailed API information. That page also supports "Try it out!" links so that you can experiment with the API right from the documentation. The documentation is intended to show how to call the API endpoints via either raw RESTful https requests or using the SDK.
10
+
11
+ Keep in mind that all API calls are done 'as' the user whose credentials were used to login to the API. The Looker permissioning system enforces various rules about which activities users with various permissions are and are not allowed to do; and data they are or are not allowed to access. For instance, there are many configuration and looker management activities that only Admin users are allowed to perform; like creating and asigning user roles. Additionally, non-admin users have very limited access to information about other users.
12
+
13
+ When trying to access a resource with the API that the current user is not allowed to access, the API will return a '404 Not Found' error - the same as if the resource did not exist at all. This is a standard practice for RESTful services. By default, the Ruby SDK will convert all non-success result codes into ruby exceptions which it then raises. So, error paths are handled by rescuing exceptions rather than checking result codes for each SDK request.
14
+
15
+ ### Installation
16
+ ```bash
17
+ $ git clone git@github.com:looker/looker-sdk-ruby.git looker-sdk
18
+ $ cd looker-sdk
19
+ $ gem install bundle
20
+ $ bundle install
21
+ $ rake install
22
+ ```
23
+
24
+ ### Development
25
+
26
+ ```bash
27
+ $ bundle install
28
+ $ rake test # run the test suite
29
+ $ rake test:all # run the test suite on all supported Rubies
30
+ ```
31
+
32
+ ### Basic Usage
33
+
34
+ ```ruby
35
+ require 'looker-sdk'
36
+
37
+ # An sdk client can be created with an explicit client_id/client_secret pair
38
+ # (this is discouraged because secrets in code files can easily lead to those secrets being compromised!)
39
+ sdk = LookerSDK::Client.new(
40
+ :client_id => "4CN7jzm7yrkcy2MC4CCG",
41
+ :client_secret => "Js3rZZ7vHfbc2hBynSj7zqKh",
42
+ :api_endpoint => "https://mygreatcompany.looker.com:19999/api/3.0"
43
+ )
44
+
45
+ # If you don't want to provide explicit credentials: (trust me you don't)
46
+ # add the below to your ~/.netrc file (or create the file if you don't have one).
47
+ # Note that to use netrc you need to install the netrc ruby gem.
48
+ #
49
+ # machine mygreatcompany.looker.com
50
+ # login my_client_id
51
+ # password my_client_secret
52
+
53
+ sdk = LookerSDK::Client.new(
54
+ :netrc => true,
55
+ :netrc_file => "~/.net_rc",
56
+ :api_endpoint => "https://mygreatcompany.looker.com:19999/api/3.0",
57
+
58
+ # Disable cert verification if the looker has a self-signed cert.
59
+ # Avoid this if using real certificates; verification of the server cert is a very good thing for production.
60
+ # :connection_options => {:ssl => {:verify => false}},
61
+
62
+ # Set longer timeout to allow for long running queries.
63
+ # :connection_options => {:request => {:timeout => 60 * 60, :open_timeout => 30}},
64
+
65
+ # Support self-signed cert *and* set longer timeout to allow for long running queries.
66
+ # :connection_options => {:ssl => {:verify => false}, :request => {:timeout => 60 * 60, :open_timeout => 30}},
67
+ )
68
+
69
+ # Check if we can even communicate with the Looker - without even trying to authenticate.
70
+ # This will throw an exception if the sdk can't connect at all. This can help a lot with debugging your
71
+ # first attempts at using the sdk.
72
+ sdk.alive
73
+
74
+ # Supports user creation, modification, deletion
75
+ # Supports email_credentials creation, modification, and deletion.
76
+
77
+ first_user = sdk.create_user({:first_name => "Jonathan", :last_name => "Swenson"})
78
+ sdk.create_user_credentials_email(first_user[:id], {:email => "jonathan@example.com"})
79
+
80
+ second_user = sdk.create_user({:first_name => "John F", :last_name => "Kennedy"})
81
+ sdk.create_user_credentials_email(second_user[:id], {:email => "john@example.com"})
82
+
83
+ third_user = sdk.create_user({:first_name => "Frank", :last_name => "Sinatra"})
84
+ sdk.create_user_credentials_email(third_user[:id], {:email => "frank@example.com"})
85
+
86
+ user = sdk.user(first_user[:id])
87
+ user.first_name # Jonathan
88
+ user.last_name # Swenson
89
+
90
+ sdk.update_user(first_user[:id], {:first_name => "Jonathan is awesome"})
91
+ user = sdk.user(first_user[:id])
92
+ user.first_name # "Jonathan is awesome"
93
+
94
+ credentials_email = sdk.user_credentials_email(user[:id])
95
+ credentials_email[:email] # jonathan@example.com
96
+
97
+ sdk.update_user_credentials_email(user[:id], {:email => "jonathan+1@example.com"})
98
+ credentials_email = sdk.user_credentials_email(user[:id])
99
+ credentials_email[:email] # jonathan+1@example.com
100
+
101
+ users = sdk.all_users()
102
+ users.length # 3
103
+ users[0] # first_user
104
+
105
+
106
+ sdk.delete_user_credentials_email(second_user[:id])
107
+ sdk.delete_user(second_user[:id])
108
+
109
+ users = sdk.all_users()
110
+ users.length # 2
111
+ users[1] # third_user
112
+
113
+ ```
114
+
115
+ ### TODO
116
+ Things that we think are important to do will be marked with `look TODO`
117
+
@@ -0,0 +1,41 @@
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
+ tmp
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'pry', '~> 0.10.1'
4
+ gem 'netrc', '~> 0.7.7'
5
+ # gem 'looker-sdk', :git => 'git@github.com:looker/looker-sdk-ruby.git'
6
+ gem 'looker-sdk', :path => '../'
@@ -0,0 +1,18 @@
1
+ # [Looker](http://looker.com/) SDK Ruby Shell
2
+
3
+ The Looker SDK Shell allows you to call and experiment with Looker SDK APIs
4
+ in a [REPL](https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop) interactive command-line environment.
5
+
6
+ ### SDK Shell Setup
7
+ ```bash
8
+ $ cd looker-sdk/shell
9
+ $ bundle install
10
+ ```
11
+
12
+ ### Running the Shell
13
+ ```bash
14
+ $ ruby shell.rb
15
+ ```
16
+ The Looker SDK Shell expects to find Looker API authentication credentials in
17
+ a .netrc file in the current directory. See the [Looker SDK readme](../readme.md) for details
18
+ on setting up your .netrc file.
@@ -0,0 +1,37 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+
4
+ require 'looker-sdk.rb'
5
+ require 'pry'
6
+
7
+ def sdk
8
+ @sdk ||= LookerSDK::Client.new(
9
+ # Create your own API3 key and add it to a .netrc file in your location of choice.
10
+ :netrc => true,
11
+ :netrc_file => "./.netrc",
12
+
13
+ # Disable cert verification if the looker has a self-signed cert.
14
+ # :connection_options => {:ssl => {:verify => false}},
15
+
16
+ # Support self-signed cert *and* set longer timeout to allow for long running queries.
17
+ :connection_options => {:ssl => {:verify => false}, :request => {:timeout => 60 * 60, :open_timeout => 30}},
18
+
19
+ :api_endpoint => "https://self-signed.looker.com:19999/api/3.0",
20
+
21
+ # Customize to use your specific looker instance
22
+ # :connection_options => {:ssl => {:verify => true}},
23
+ # :api_endpoint => "https://looker.mycoolcompany.com:19999/api/3.0",
24
+ )
25
+ end
26
+
27
+ begin
28
+ puts "Connecting to Looker at '#{sdk.api_endpoint}'"
29
+ puts (code = sdk.alive) == 200 ? "Looker is alive!" : "Sad Looker: #{code}"
30
+
31
+ binding.pry self
32
+ rescue Exception => e
33
+ puts e
34
+ ensure
35
+ puts 'Bye!'
36
+ end
37
+
@@ -0,0 +1,59 @@
1
+ ### Streaming Downloads
2
+
3
+ #### Beta Feature - Experimental!
4
+
5
+ This SDK makes it easy to fetch a response from a Looker API and hydrate it into a Ruby object.This convenience is great for working with configuration and administrative data. However, when the response is gigabytes of row data, pulling it all into memory doesn't work so well - you can't begin processing the data until after it has all downloaded, for example, and chewing up tons of memory will put a serious strain on the entire system - even crash it.
6
+
7
+ One solution to all this is to use streaming downloads to process the data in chunks as it is downloaded. Streaming requires a little more code to set up but the benefits can be significant.
8
+
9
+ To use streaming downloads with the Looker SDK, simply add a block to an SDK call. The block will be called with chunks of data as they are downloaded instead of the method returning a complete result.
10
+
11
+ For example:
12
+
13
+ ```ruby
14
+ def run_look_to_file(look_id, filename, format, opts = {})
15
+ File.open(filename, 'w') do |file|
16
+ sdk.run_look(look_id, format, opts) do |data, progress|
17
+ file.write(data)
18
+ puts "Wrote #{data.length} bytes of #{progress.length} total"
19
+ end
20
+ end
21
+ end
22
+
23
+ run_look_to_file(38, 'out.csv', 'csv', limit: 10000)
24
+ ```
25
+
26
+ In the code above, `sdk.run_look` opens a statement block. The code in the block will be called with chunks of data. The output looks like this:
27
+ ```
28
+ Wrote 16384 bytes of 16384 total
29
+ Wrote 16384 bytes of 32768 total
30
+ Wrote 7327 bytes of 40095 total
31
+ Wrote 16384 bytes of 56479 total
32
+ etc...
33
+ ```
34
+
35
+ You can also abort a streaming download by calling `progress.stop` within the block, like this:
36
+ ```ruby
37
+ sdk.run_look(look_id, format, opts) do |data, progress|
38
+ if some_condition
39
+ progress.stop
40
+ else
41
+ process_data(data)
42
+ end
43
+ end
44
+
45
+ ```
46
+
47
+ ##### Streaming Caveats
48
+ * You won't know in advance how many bytes are in the response. Blocks arrive until there aren't any more.
49
+ * If the connection to the Looker server is broken while streaming, it will have the same appearance as normal end-of-file stream termination.
50
+ * The HTTP status in the response arrives before the response body begins downloading. If an error occurs during the download, it cannot be communicated via HTTP status. It is quite possible to have an HTTP status 200 OK and later discover an error in the data stream. If the connection between the Looker server and the SQL database is severed while you are streaming results, for example, Looker will append an error message to the data you receive and terminate the streaming session.
51
+
52
+ These caveats can be mitigated by knowing the structure of the data being streamed. Row data in JSON format will be an array of objects, for example. If the data received is missing the closing `]` then you know the stream download ended prematurely.
53
+
54
+ #### A Tale of Two Stacks
55
+
56
+ The Looker Ruby SDK is built on top of Sawyer, and Sawyer sits on top of Faraday. Faraday does not support HTTP Streaming, so to do our streaming stuff we have to bypass Faraday and talk directly to the Net:HTTP stack. Our streaming implementation gathers connection settings from the Faraday stack so there's no additional config required for http streaming.
57
+
58
+ Streaming downloads have not been tested with proxy connections. We assume attempting to stream across an http proxy will not work.
59
+
@@ -0,0 +1,46 @@
1
+ require 'simplecov'
2
+ SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
3
+ SimpleCov::Formatter::HTMLFormatter
4
+ ]
5
+ SimpleCov.start
6
+
7
+ require 'rubygems'
8
+ require 'bundler/setup'
9
+
10
+ require 'ostruct'
11
+ require 'json'
12
+ require 'looker-sdk'
13
+
14
+ require 'minitest/autorun'
15
+ require 'minitest/spec'
16
+ require 'minitest/mock'
17
+ require 'mocha/mini_test'
18
+ require "rack/test"
19
+ require "rack/request"
20
+
21
+ def fixture_path
22
+ File.expand_path("../fixtures", __FILE__)
23
+ end
24
+
25
+ def fix_netrc_permissions(path)
26
+ s = File.stat(path)
27
+ raise "'#{path}'' not a file" unless s.file?
28
+ File.chmod(0600, path) unless s.mode.to_s(8)[3..5] == "0600"
29
+ end
30
+
31
+ fix_netrc_permissions(File.join(fixture_path, '.netrc'))
32
+
33
+ def setup_sdk
34
+ LookerSDK.reset!
35
+ LookerSDK.configure do |c|
36
+ c.connection_options = {:ssl => {:verify => false}}
37
+ c.netrc = true
38
+ c.netrc_file = File.join(fixture_path, '.netrc')
39
+ end
40
+ end
41
+
42
+ def teardown_sdk
43
+ LookerSDK.logout
44
+ end
45
+
46
+