tableau_rest_api 0.1.2
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/Gemfile +8 -0
- data/Guardfile +21 -0
- data/LICENSE.txt +21 -0
- data/README.md +103 -0
- data/Rakefile +10 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/fixtures/vcr_cassettes/add_user.yml +223 -0
- data/fixtures/vcr_cassettes/add_user_to_group.yml +451 -0
- data/fixtures/vcr_cassettes/add_user_to_group_through_site.yml +451 -0
- data/fixtures/vcr_cassettes/create_group.yml +224 -0
- data/fixtures/vcr_cassettes/create_group_through_site.yml +224 -0
- data/fixtures/vcr_cassettes/datasources.yml +234 -0
- data/fixtures/vcr_cassettes/datasources_through_site.yml +234 -0
- data/fixtures/vcr_cassettes/delete_datasource.yml +334 -0
- data/fixtures/vcr_cassettes/delete_group.yml +328 -0
- data/fixtures/vcr_cassettes/delete_group_through_site.yml +329 -0
- data/fixtures/vcr_cassettes/delete_schedule.yml +276 -0
- data/fixtures/vcr_cassettes/delete_site.yml +214 -0
- data/fixtures/vcr_cassettes/delete_subscription.yml +329 -0
- data/fixtures/vcr_cassettes/delete_workbook.yml +350 -0
- data/fixtures/vcr_cassettes/get_datasource.yml +673 -0
- data/fixtures/vcr_cassettes/get_workbook.yml +15044 -0
- data/fixtures/vcr_cassettes/groups.yml +229 -0
- data/fixtures/vcr_cassettes/groups_through_site.yml +230 -0
- data/fixtures/vcr_cassettes/projects_through_site.yml +230 -0
- data/fixtures/vcr_cassettes/query_workbooks.yml +250 -0
- data/fixtures/vcr_cassettes/remove_user_from_group_through_site.yml +440 -0
- data/fixtures/vcr_cassettes/remove_user_from_site_through_site.yml +329 -0
- data/fixtures/vcr_cassettes/schedules.yml +115 -0
- data/fixtures/vcr_cassettes/server_info.yml +53 -0
- data/fixtures/vcr_cassettes/signin.yml +57 -0
- data/fixtures/vcr_cassettes/signout.yml +102 -0
- data/fixtures/vcr_cassettes/sites.yml +118 -0
- data/fixtures/vcr_cassettes/subscriptions.yml +226 -0
- data/fixtures/vcr_cassettes/switch_site.yml +113 -0
- data/fixtures/vcr_cassettes/update_group_through_site.yml +343 -0
- data/fixtures/vcr_cassettes/update_user_through_site.yml +333 -0
- data/fixtures/vcr_cassettes/users_in_group.yml +339 -0
- data/fixtures/vcr_cassettes/users_on_site.yml +230 -0
- data/fixtures/vcr_cassettes/users_through_site.yml +230 -0
- data/fixtures/vcr_cassettes/workbooks_through_site.yml +250 -0
- data/lib/tableau_rest_api.rb +32 -0
- data/lib/tableau_rest_api/api_error.rb +7 -0
- data/lib/tableau_rest_api/area/schedule_subscription.rb +41 -0
- data/lib/tableau_rest_api/area/user_group.rb +72 -0
- data/lib/tableau_rest_api/area/workbook_datasource.rb +87 -0
- data/lib/tableau_rest_api/client.rb +65 -0
- data/lib/tableau_rest_api/helpers/helpers.rb +9 -0
- data/lib/tableau_rest_api/resource.rb +42 -0
- data/lib/tableau_rest_api/resources/base.rb +24 -0
- data/lib/tableau_rest_api/resources/datasource.rb +12 -0
- data/lib/tableau_rest_api/resources/group.rb +10 -0
- data/lib/tableau_rest_api/resources/project.rb +12 -0
- data/lib/tableau_rest_api/resources/schedule.rb +12 -0
- data/lib/tableau_rest_api/resources/server.rb +12 -0
- data/lib/tableau_rest_api/resources/site.rb +68 -0
- data/lib/tableau_rest_api/resources/subscription.rb +10 -0
- data/lib/tableau_rest_api/resources/user.rb +12 -0
- data/lib/tableau_rest_api/resources/workbook.rb +12 -0
- data/lib/tableau_rest_api/util/config.rb +22 -0
- data/lib/tableau_rest_api/util/response.rb +11 -0
- data/lib/tableau_rest_api/util/token.rb +16 -0
- data/lib/tableau_rest_api/util/upload.rb +29 -0
- data/lib/tableau_rest_api/version.rb +3 -0
- data/tableau_rest_api.gemspec +33 -0
- metadata +222 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
module TableauRestApi
|
|
2
|
+
# Client class wrapping a subset of the Tableau Rest API
|
|
3
|
+
# This class just contains worker methods for interacting with Tableau.
|
|
4
|
+
# See the Request sub-class for resource requests.
|
|
5
|
+
class Client
|
|
6
|
+
attr_reader :config, :token
|
|
7
|
+
|
|
8
|
+
def initialize
|
|
9
|
+
@config = Config.new.options
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def configure(config)
|
|
13
|
+
@config = Config.new(config).options
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def login(*_args)
|
|
17
|
+
return if authorised?
|
|
18
|
+
url = build_url ['auth', 'signin']
|
|
19
|
+
resp = post url, @config[:credentials]
|
|
20
|
+
@token = Token.new(resp.credentials.token, @config[:auth_duration])
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def logout
|
|
24
|
+
post(build_url ['auth', 'signout'])
|
|
25
|
+
@token = nil
|
|
26
|
+
!authorised?
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def authorised?
|
|
30
|
+
token = self.token
|
|
31
|
+
token && !token.expired? ? true : false
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
def header(boundary=nil)
|
|
37
|
+
head = { :accept => :json, :content_type => :json }
|
|
38
|
+
head = authorised? ? head.merge({ :x_tableau_auth => self.token.value }) : head
|
|
39
|
+
head = boundary ? head.merge({ :content_type => "multipart/mixed; boundary=#{boundary}"}) : head
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def build_url(endpoint)
|
|
43
|
+
endpoint = endpoint.is_a?(Array) ? endpoint.join('/') : endpoint
|
|
44
|
+
"#{@config[:host]}/#{@config[:base]}/#{@config[:api_version]}/#{endpoint}"
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def get(url)
|
|
48
|
+
Response.new(RestClient.get(url, header)).parse
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def post(url, data={}, boundary=nil)
|
|
52
|
+
data = data.to_json unless boundary
|
|
53
|
+
headers = header boundary
|
|
54
|
+
Response.new(RestClient.post(url, data, headers)).parse
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def put(url, data={})
|
|
58
|
+
Response.new(RestClient.put(url, data.to_json, header)).parse
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def delete(url)
|
|
62
|
+
Response.new(RestClient.delete(url, header)).parse
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
module TableauRestApi
|
|
2
|
+
# Subclass providing access to Tableau resources.
|
|
3
|
+
# An auth token is requested if missing or expired.
|
|
4
|
+
class Resource < Client
|
|
5
|
+
aspector do
|
|
6
|
+
before :sites, :login
|
|
7
|
+
before :create_site, :login
|
|
8
|
+
before :switch_site, :login
|
|
9
|
+
before :delete_site, :login
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
include TableauRestApi::UserGroup
|
|
13
|
+
include TableauRestApi::WorkbookDatasource
|
|
14
|
+
include TableauRestApi::ScheduleSubscription
|
|
15
|
+
|
|
16
|
+
def server_info
|
|
17
|
+
url = build_url 'serverinfo'
|
|
18
|
+
Server.new((get url).serverInfo)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def sites
|
|
22
|
+
url = build_url 'sites'
|
|
23
|
+
(get url).sites.site.to_a.map { |site| Site.new(site, self) }
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def create_site(site)
|
|
27
|
+
url = build_url 'sites'
|
|
28
|
+
Site.new((post url, site).site, self)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def switch_site(site)
|
|
32
|
+
url = build_url ['auth', 'switchSite']
|
|
33
|
+
@token = Token.new((post url, site).credentials.token, self.config[:auth_duration])
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def delete_site(site_id)
|
|
37
|
+
url = build_url ['sites', site_id]
|
|
38
|
+
delete url
|
|
39
|
+
@token = nil
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module TableauRestApi
|
|
2
|
+
class Base
|
|
3
|
+
def to_hash
|
|
4
|
+
self.to_array.to_h
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def to_h
|
|
8
|
+
self.to_hash
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def to_array
|
|
12
|
+
filter_instance_variables.map do |var|
|
|
13
|
+
[Helpers::camel_case_lower(var[1..-1]).to_sym, self.instance_variable_get(var)]
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
def filter_instance_variables
|
|
20
|
+
self.instance_variables.select { |var| var != :@called_by }
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
module TableauRestApi
|
|
2
|
+
class Project < Base
|
|
3
|
+
attr_reader :id, :name, :description, :content_permissions
|
|
4
|
+
|
|
5
|
+
def initialize(project)
|
|
6
|
+
@id = project.id
|
|
7
|
+
@name = project.name
|
|
8
|
+
@description = project.description
|
|
9
|
+
@content_permissions = project.contentPermissions
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
module TableauRestApi
|
|
2
|
+
class Server < Base
|
|
3
|
+
attr_reader :version, :build , :api_version
|
|
4
|
+
|
|
5
|
+
def initialize(server)
|
|
6
|
+
product_version = server.productVersion
|
|
7
|
+
@version = product_version.value
|
|
8
|
+
@build = product_version.build
|
|
9
|
+
@api_version = server.restApiVersion
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
module TableauRestApi
|
|
2
|
+
class Site < Base
|
|
3
|
+
attr_reader :id, :name, :content_url
|
|
4
|
+
|
|
5
|
+
def initialize(site, called_by)
|
|
6
|
+
@id = site.id
|
|
7
|
+
@name = site.name
|
|
8
|
+
@content_url = site.contentUrl
|
|
9
|
+
@called_by = called_by
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def users
|
|
13
|
+
@called_by.users_on_site(@id)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def groups
|
|
17
|
+
@called_by.groups(@id)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def datasources
|
|
21
|
+
@called_by.datasources(@id)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def projects
|
|
25
|
+
@called_by.query_projects(@id)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def workbooks
|
|
29
|
+
@called_by.query_workbooks(@id)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def users_in_group(group_id)
|
|
33
|
+
@called_by.users_in_group(@id, group_id)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def create_group(group)
|
|
37
|
+
@called_by.create_group(@id, group)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def delete_group(group_id)
|
|
41
|
+
@called_by.delete_group(@id, group_id)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def add_user_to_group(group_id, user_id)
|
|
45
|
+
@called_by.add_user_to_group(@id, group_id, user_id)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def add_user_to_site(user)
|
|
49
|
+
@called_by.add_user_to_site(@id, user)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def remove_user_from_group(group_id, user_id)
|
|
53
|
+
@called_by.remove_user_from_group(@id, group_id, user_id)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def remove_user_from_site(user_id)
|
|
57
|
+
@called_by.remove_user_from_site(@id, user_id)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def update_user(user)
|
|
61
|
+
@called_by.update_user(@id, user)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def update_group(group)
|
|
65
|
+
@called_by.update_group(@id, group)
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
module TableauRestApi
|
|
2
|
+
class Workbook < Base
|
|
3
|
+
attr_reader :id, :name, :content_url, :project
|
|
4
|
+
|
|
5
|
+
def initialize(workbook)
|
|
6
|
+
@id = workbook.id
|
|
7
|
+
@name = workbook.name
|
|
8
|
+
@content_url = workbook.contentUrl
|
|
9
|
+
@project = Project.new(workbook.project)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
module TableauRestApi
|
|
2
|
+
class Config
|
|
3
|
+
attr_reader :options
|
|
4
|
+
|
|
5
|
+
def initialize(config={})
|
|
6
|
+
@options = {
|
|
7
|
+
host: ENV['TABLEAU_HOST'] || 'https://tableau.lvh.me',
|
|
8
|
+
api_version: '2.6',
|
|
9
|
+
base: 'api',
|
|
10
|
+
auth_duration: ENV['TABLEAU_AUTH_TOKEN_DURATION'] || 4.hours,
|
|
11
|
+
credentials: {
|
|
12
|
+
credentials: {
|
|
13
|
+
name: ENV['TABLEAU_USERNAME'] || 'admin',
|
|
14
|
+
password: ENV['TABLEAU_PASSWORD'] || 'admin',
|
|
15
|
+
site: { contentUrl: "" }
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
@options = @options.merge(config)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module TableauRestApi
|
|
2
|
+
# Representation of a x_auth_tableau token
|
|
3
|
+
# These tokens have a finite lifespan, the default is 4 hours.
|
|
4
|
+
class Token
|
|
5
|
+
attr_reader :value, :expires
|
|
6
|
+
|
|
7
|
+
def initialize(token, duration)
|
|
8
|
+
@value = token
|
|
9
|
+
@expires = duration.from_now
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def expired?
|
|
13
|
+
@value ? Time.now > @expires : true
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module TableauRestApi
|
|
2
|
+
class Upload
|
|
3
|
+
def initialize(metadata, payload, boundary)
|
|
4
|
+
@metadata = metadata
|
|
5
|
+
@payload = payload
|
|
6
|
+
@boundary = boundary
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def build(obj=:workbook)
|
|
10
|
+
<<-END
|
|
11
|
+
--#{@boundary}\r
|
|
12
|
+
Content-Disposition: name="request_payload"\r
|
|
13
|
+
Content-Type: text/xml\r
|
|
14
|
+
\r
|
|
15
|
+
<tsRequest>\r
|
|
16
|
+
<#{obj} name="#{@metadata[:name]}">\r
|
|
17
|
+
<project id="#{@metadata[:project]}"/>\r
|
|
18
|
+
</#{obj}>\r
|
|
19
|
+
</tsRequest>\r
|
|
20
|
+
--#{@boundary}\r
|
|
21
|
+
Content-Disposition: name="tableau_#{obj}"; filename="#{@payload[:filename]}"\r
|
|
22
|
+
Content-Type: application/octet-stream\r
|
|
23
|
+
\r
|
|
24
|
+
#{@payload[:data]}\r
|
|
25
|
+
--#{@boundary}--\r
|
|
26
|
+
END
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
|
+
require 'tableau_rest_api/version'
|
|
5
|
+
|
|
6
|
+
Gem::Specification.new do |spec|
|
|
7
|
+
spec.name = "tableau_rest_api"
|
|
8
|
+
spec.version = TableauRestApi::VERSION
|
|
9
|
+
spec.authors = ["Dave Lancaster"]
|
|
10
|
+
spec.email = ["lancaster.dave@gmail.com"]
|
|
11
|
+
|
|
12
|
+
spec.summary = %q{Ruby library wrapping the Tableau REST API.}
|
|
13
|
+
spec.description = %q{Exposes a subset of the Tablea v2.6 REST API (json).}
|
|
14
|
+
spec.homepage = "https://github.com/davelancaster/tableau-rest-api"
|
|
15
|
+
spec.license = "MIT"
|
|
16
|
+
|
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
|
18
|
+
f.match(%r{^(test|spec|features)/})
|
|
19
|
+
end
|
|
20
|
+
spec.bindir = "exe"
|
|
21
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
22
|
+
spec.require_paths = ["lib"]
|
|
23
|
+
|
|
24
|
+
spec.add_development_dependency "bundler", "~> 1.14"
|
|
25
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
|
26
|
+
spec.add_development_dependency "minitest", "~> 5.0"
|
|
27
|
+
spec.add_development_dependency "vcr", "~> 3.0"
|
|
28
|
+
spec.add_development_dependency "webmock", "~> 3.0"
|
|
29
|
+
|
|
30
|
+
spec.add_dependency "rest-client", "~> 2.0"
|
|
31
|
+
spec.add_dependency "aspector", "~> 0.14"
|
|
32
|
+
spec.add_dependency "timerizer", "~> 0.1"
|
|
33
|
+
end
|