tableau_server_client 0.0.3
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 +4 -0
- data/Gemfile.lock +51 -0
- data/README.md +1 -0
- data/Rakefile +6 -0
- data/bin/console +9 -0
- data/lib/tableau-server-client/client.rb +120 -0
- data/lib/tableau-server-client/paginatable_response.rb +73 -0
- data/lib/tableau-server-client/request_builder.rb +16 -0
- data/lib/tableau-server-client/request_url.rb +29 -0
- data/lib/tableau-server-client/resources/connection.rb +37 -0
- data/lib/tableau-server-client/resources/datasource.rb +35 -0
- data/lib/tableau-server-client/resources/project.rb +35 -0
- data/lib/tableau-server-client/resources/resource.rb +88 -0
- data/lib/tableau-server-client/resources/schedule.rb +24 -0
- data/lib/tableau-server-client/resources/site.rb +42 -0
- data/lib/tableau-server-client/resources/workbook.rb +35 -0
- data/lib/tableau-server-client/server.rb +37 -0
- data/lib/tableau-server-client/token.rb +36 -0
- data/lib/tableau-server-client/version.rb +3 -0
- data/tableau_server_client.gemspec +32 -0
- metadata +163 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 492e70dc7a49eb33db9318b3ace0065b36cdb19d
|
4
|
+
data.tar.gz: d0bddfa685157808e77380898bc05d74bedacd89
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 311e2452bd507ab1359e22bf690f835f6849e26bc7b689efd6e0c386e4cfadaf2952b5528189d474368d772193fd963386b686965f74f8ab0a4f8988a11e29e2
|
7
|
+
data.tar.gz: 17f5966cfb76ef52acb0748ff98209a279a951eb6bffd2416e6e6d6ef66648216afb2c2abe1e52d993a5b0ccaa850cfe4cac94fe6e96cd9953a0abb10ac2c4aa
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
tableau_server_client (0.0.1)
|
5
|
+
faraday (>= 0.14.0)
|
6
|
+
nokogiri (>= 1.8.2)
|
7
|
+
pry (>= 0.11.3)
|
8
|
+
|
9
|
+
GEM
|
10
|
+
remote: https://rubygems.org/
|
11
|
+
specs:
|
12
|
+
byebug (10.0.2)
|
13
|
+
coderay (1.1.2)
|
14
|
+
diff-lcs (1.3)
|
15
|
+
faraday (0.14.0)
|
16
|
+
multipart-post (>= 1.2, < 3)
|
17
|
+
method_source (0.9.0)
|
18
|
+
mini_portile2 (2.3.0)
|
19
|
+
multipart-post (2.0.0)
|
20
|
+
nokogiri (1.8.2)
|
21
|
+
mini_portile2 (~> 2.3.0)
|
22
|
+
pry (0.11.3)
|
23
|
+
coderay (~> 1.1.0)
|
24
|
+
method_source (~> 0.9.0)
|
25
|
+
rake (10.5.0)
|
26
|
+
rspec (3.7.0)
|
27
|
+
rspec-core (~> 3.7.0)
|
28
|
+
rspec-expectations (~> 3.7.0)
|
29
|
+
rspec-mocks (~> 3.7.0)
|
30
|
+
rspec-core (3.7.1)
|
31
|
+
rspec-support (~> 3.7.0)
|
32
|
+
rspec-expectations (3.7.0)
|
33
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
34
|
+
rspec-support (~> 3.7.0)
|
35
|
+
rspec-mocks (3.7.0)
|
36
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
37
|
+
rspec-support (~> 3.7.0)
|
38
|
+
rspec-support (3.7.1)
|
39
|
+
|
40
|
+
PLATFORMS
|
41
|
+
ruby
|
42
|
+
|
43
|
+
DEPENDENCIES
|
44
|
+
bundler (~> 1.13)
|
45
|
+
byebug
|
46
|
+
rake (~> 10.0)
|
47
|
+
rspec (~> 3.0)
|
48
|
+
tableau_server_client!
|
49
|
+
|
50
|
+
BUNDLED WITH
|
51
|
+
1.16.0.pre.3
|
data/README.md
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
# tableau-client
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'faraday'
|
3
|
+
require 'nokogiri'
|
4
|
+
require 'tableau-server-client/request_url'
|
5
|
+
require 'tableau-server-client/request_builder'
|
6
|
+
require 'tableau-server-client/token'
|
7
|
+
require 'tableau-server-client/paginatable_response'
|
8
|
+
|
9
|
+
module TableauServerClient
|
10
|
+
|
11
|
+
class Client
|
12
|
+
include RequestBuilder
|
13
|
+
|
14
|
+
def initialize(server_url, username, password, site_name, api_version, token_lifetime, logger)
|
15
|
+
@server_url = server_url
|
16
|
+
@username = username
|
17
|
+
@password = password
|
18
|
+
@site_name = site_name
|
19
|
+
@api_version = api_version
|
20
|
+
@token_lifetime = token_lifetime
|
21
|
+
@logger
|
22
|
+
end
|
23
|
+
|
24
|
+
attr_reader :site_name, :username, :api_version, :token_lifetime, :logger
|
25
|
+
|
26
|
+
def server_url
|
27
|
+
@_server_url ||= URI(@server_url.chomp("/"))
|
28
|
+
end
|
29
|
+
|
30
|
+
def get_collection(resource_location, &block)
|
31
|
+
return self.to_enum(:get_collection, resource_location) unless block
|
32
|
+
req_url = request_url(resource_location.path, resource_location.query_params)
|
33
|
+
response = session.get req_url.to_s
|
34
|
+
TableauServerClient::PaginatableResponse.new(self, req_url, response).each_body do |b|
|
35
|
+
resource_location.klass.from_collection_response(self, resource_location.path, b) {|r| yield r }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def get(resource_location)
|
40
|
+
req_url = request_url(resource_location.path)
|
41
|
+
response = session.get req_url.to_s
|
42
|
+
resource_location.klass.from_response(self, resource_location.path, Nokogiri::XML(response.body))
|
43
|
+
end
|
44
|
+
|
45
|
+
def create(resource)
|
46
|
+
end
|
47
|
+
|
48
|
+
def delete(resource)
|
49
|
+
end
|
50
|
+
|
51
|
+
def update(resource)
|
52
|
+
session.put do |req|
|
53
|
+
req.url request_url(resource.path).to_s
|
54
|
+
req.body = resource.to_request
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def session
|
59
|
+
faraday.headers['X-Tableau-Auth'] = token.to_s
|
60
|
+
faraday
|
61
|
+
end
|
62
|
+
|
63
|
+
def token
|
64
|
+
unless @token and @token.valid?
|
65
|
+
@token = signin
|
66
|
+
end
|
67
|
+
@token
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
attr_reader :password
|
73
|
+
|
74
|
+
def request_url(path, query_params={})
|
75
|
+
RequestUrl.new(server_url, api_version, path, query_params)
|
76
|
+
end
|
77
|
+
|
78
|
+
def request_body(&block)
|
79
|
+
build_request &block
|
80
|
+
end
|
81
|
+
|
82
|
+
def signin
|
83
|
+
request = request_body {|b|
|
84
|
+
b.credentials(name: username, password: password) {
|
85
|
+
b.site(contentUrl: content_url)
|
86
|
+
}
|
87
|
+
}
|
88
|
+
# POST without Token
|
89
|
+
res = faraday.post do |req|
|
90
|
+
req.url request_url("auth/signin").to_s
|
91
|
+
req.body = request
|
92
|
+
end
|
93
|
+
@token = TableauServerClient::Token.parse(res.body, token_lifetime)
|
94
|
+
end
|
95
|
+
|
96
|
+
def content_url
|
97
|
+
site_name == 'default' ? "" : site_name
|
98
|
+
end
|
99
|
+
|
100
|
+
def faraday
|
101
|
+
@faraday ||= Faraday.new(request: {params_encoder: EmptyEncoder.new}, headers: {'Content-Type' => 'application/xml'}) do |f|
|
102
|
+
f.response :raise_error
|
103
|
+
#TODO Cannot set log level (always print debug log)
|
104
|
+
#f.response :logger, logger
|
105
|
+
f.adapter Faraday.default_adapter
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
class EmptyEncoder
|
110
|
+
def encode(hash)
|
111
|
+
hash.keys.map {|k| "#{k}=#{hash[k]}" }.join('&')
|
112
|
+
end
|
113
|
+
|
114
|
+
def decode(str)
|
115
|
+
str.split('&').map {|p| p.split('=') }.to_h
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
|
3
|
+
module TableauServerClient
|
4
|
+
class PaginatableResponse
|
5
|
+
include Enumerable
|
6
|
+
|
7
|
+
def initialize(client, request_url, response)
|
8
|
+
@client = client
|
9
|
+
@request_url = request_url
|
10
|
+
@response = response
|
11
|
+
end
|
12
|
+
|
13
|
+
def each
|
14
|
+
yield @response
|
15
|
+
return unless paginated?
|
16
|
+
res = @response.dup
|
17
|
+
url = @request_url.dup
|
18
|
+
while true
|
19
|
+
pgn = Pagination.parse(res.body)
|
20
|
+
break unless pgn.next_page?
|
21
|
+
res = @client.session.get url.merge_params!(pgn.request_params).to_s
|
22
|
+
yield res
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def each_body
|
27
|
+
each do |res|
|
28
|
+
yield Nokogiri::XML(res.body)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def paginated?
|
35
|
+
@paginated ||= !Pagination.parse(@response.body).nil?
|
36
|
+
end
|
37
|
+
|
38
|
+
class Pagination
|
39
|
+
def initialize(page_number, page_size, total_available)
|
40
|
+
@page_numner = page_number
|
41
|
+
@page_size = page_size
|
42
|
+
@total_available = total_available
|
43
|
+
end
|
44
|
+
|
45
|
+
def page_number
|
46
|
+
@page_number.to_i
|
47
|
+
end
|
48
|
+
|
49
|
+
def page_size
|
50
|
+
@page_size.to_i
|
51
|
+
end
|
52
|
+
|
53
|
+
def total_available
|
54
|
+
@total_available.to_i
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.parse(xml)
|
58
|
+
pg = Nokogiri::XML(xml).xpath("//xmlns:pagination")[0]
|
59
|
+
return nil unless pg
|
60
|
+
Pagination.new(pg['pageNumber'], pg['pageSize'], pg['totalAvailable'])
|
61
|
+
end
|
62
|
+
|
63
|
+
def next_page?
|
64
|
+
page_number * page_size > total_available
|
65
|
+
end
|
66
|
+
|
67
|
+
def request_params
|
68
|
+
{ pageSize: page_size, pageNumber: page_number + 1 }
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'uri'
|
2
|
+
|
3
|
+
module TableauServerClient
|
4
|
+
class RequestUrl
|
5
|
+
def initialize(url, api_version, path, params)
|
6
|
+
@url = url
|
7
|
+
@api_version = api_version
|
8
|
+
@path = path
|
9
|
+
@params = params
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_reader :url, :api_version
|
13
|
+
attr_accessor :path, :params
|
14
|
+
|
15
|
+
def merge_params!(params)
|
16
|
+
@params.merge!(params)
|
17
|
+
self
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_s
|
21
|
+
URI("#{url}/api/#{api_version}/#{path}?#{query_params}").to_s
|
22
|
+
end
|
23
|
+
|
24
|
+
def query_params
|
25
|
+
return "" if params.empty?
|
26
|
+
params.keys.map {|k| URI.encode("#{k}=#{params[k]}") }.join("&")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'tableau-server-client/resources/resource'
|
2
|
+
|
3
|
+
module TableauServerClient
|
4
|
+
module Resources
|
5
|
+
|
6
|
+
class Connection < Resource
|
7
|
+
|
8
|
+
attr_reader :id, :type, :server_address, :server_port, :user_name, :password, :embed_password
|
9
|
+
attr_writer :user_name, :password, :embed_password
|
10
|
+
|
11
|
+
def self.from_response(client, path, xml)
|
12
|
+
attrs = extract_attributes(xml)
|
13
|
+
new(client, path, attrs)
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.from_collection_response(client, path, xml)
|
17
|
+
xml.xpath("//xmlns:connections/xmlns:connection").each do |s|
|
18
|
+
id = s['id']
|
19
|
+
yield from_response(client, "#{path}/#{id}", s)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_request
|
24
|
+
request = build_request {|b|
|
25
|
+
b.connection(serverAddress: server_address, serverPort: server_port,
|
26
|
+
userName: user_name, password: password, embedPassword: embed_password)
|
27
|
+
}
|
28
|
+
end
|
29
|
+
|
30
|
+
def update!
|
31
|
+
@client.update self
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'tableau-server-client/resources/resource'
|
2
|
+
require 'tableau-server-client/resources/project'
|
3
|
+
require 'tableau-server-client/resources/connection'
|
4
|
+
|
5
|
+
module TableauServerClient
|
6
|
+
module Resources
|
7
|
+
|
8
|
+
class Datasource < Resource
|
9
|
+
|
10
|
+
attr_reader :id, :name, :content_url, :type, :created_at, :updated_at, :is_certified, :project, :owner
|
11
|
+
|
12
|
+
def self.from_response(client, path, xml)
|
13
|
+
attrs = extract_attributes(xml)
|
14
|
+
project = xml.xpath("xmlns:project")[0]
|
15
|
+
site_path = extract_site_path(path)
|
16
|
+
project_path = "#{site_path}/projects/#{project['id']}"
|
17
|
+
attrs['project'] = Project.from_response(client, project_path, project)
|
18
|
+
#TODO add owner
|
19
|
+
new(client, path, attrs)
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.from_collection_response(client, path, xml)
|
23
|
+
xml.xpath("//xmlns:datasources/xmlns:datasource").each do |s|
|
24
|
+
id = s['id']
|
25
|
+
yield from_response(client, "#{path}/#{id}", s)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def connections
|
30
|
+
@client.get_collection Connection.location(path)
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'tableau-server-client/resources/resource'
|
2
|
+
|
3
|
+
module TableauServerClient
|
4
|
+
module Resources
|
5
|
+
|
6
|
+
class Project < Resource
|
7
|
+
|
8
|
+
attr_reader :id, :name, :description, :content_permissions
|
9
|
+
|
10
|
+
def self.from_response(client, path, xml)
|
11
|
+
attrs = extract_attributes(xml)
|
12
|
+
new(client, path, attrs)
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.from_collection_response(client, path, xml)
|
16
|
+
xml.xpath("//xmlns:projects/xmlns:project").each do |s|
|
17
|
+
id = s['id']
|
18
|
+
yield from_response(client, "#{path}/#{id}", s)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def reload
|
23
|
+
prjs = @client.get_collection Project.location(site_path, filter: ["name:eq:#{name}"])
|
24
|
+
prjs.select {|p| p.id == id }.first
|
25
|
+
end
|
26
|
+
|
27
|
+
def redshift_username
|
28
|
+
if md = description.match(/^REDSHIFT_USERNAME: (.+)$/)
|
29
|
+
return md[1]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
module TableauServerClient
|
2
|
+
module Resources
|
3
|
+
|
4
|
+
class Resource
|
5
|
+
include RequestBuilder
|
6
|
+
|
7
|
+
def initialize(client, path, attributes)
|
8
|
+
@client = client
|
9
|
+
@path = path
|
10
|
+
attributes.each {|k,v| instance_variable_set("@#{k}",v) }
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.attr_reader(*vars)
|
14
|
+
@attributes ||= []
|
15
|
+
@attributes.concat (vars.map { |v| Attribute.new(v.to_s) })
|
16
|
+
super(*vars)
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.attributes
|
20
|
+
@attributes
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.resource_name
|
24
|
+
self.name.split("::").last.downcase
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.location(prefix, id=nil, filter: [])
|
28
|
+
path = [prefix, "#{resource_name}s", id].compact.join("/")
|
29
|
+
Location.new(self, path, filter)
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.extract_attributes(xml)
|
33
|
+
unless xml.name == resource_name
|
34
|
+
raise "Element name (#{xml.name}) does not match with resource name (#{resource_name})"
|
35
|
+
end
|
36
|
+
attributes.select {|a| xml.key?(a.camelCase) }.map {|a| [a.to_sym, xml[a.camelCase]] }.to_h
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.extract_site_path(path)
|
40
|
+
p = path.split('/')
|
41
|
+
p.slice(p.index('sites'),2).join('/')
|
42
|
+
end
|
43
|
+
|
44
|
+
def attributes
|
45
|
+
self.class.attributes
|
46
|
+
end
|
47
|
+
|
48
|
+
def path
|
49
|
+
@path
|
50
|
+
end
|
51
|
+
|
52
|
+
def site_path
|
53
|
+
self.class.extract_site_path(path)
|
54
|
+
end
|
55
|
+
|
56
|
+
class Location
|
57
|
+
|
58
|
+
def initialize(klass, path, filter)
|
59
|
+
@klass = klass
|
60
|
+
@path = path
|
61
|
+
@filter = filter
|
62
|
+
end
|
63
|
+
|
64
|
+
attr_reader :klass, :path
|
65
|
+
|
66
|
+
def filter
|
67
|
+
@filter.empty? ? {} : {filter: @filter.join(",")}
|
68
|
+
end
|
69
|
+
|
70
|
+
def query_params
|
71
|
+
filter
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
class Attribute < String
|
76
|
+
|
77
|
+
def camelCase
|
78
|
+
atr = self.split("_").map{ |w| w.capitalize }.join
|
79
|
+
atr[0] = atr[0].downcase
|
80
|
+
atr
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'tableau-server-client/resources/resource'
|
2
|
+
|
3
|
+
module TableauServerClient
|
4
|
+
module Resources
|
5
|
+
|
6
|
+
class Schedule < Resource
|
7
|
+
|
8
|
+
attr_reader :id, :name, :state, :priority, :created_at, :updated_at, :type, :frequency, :next_run_at
|
9
|
+
|
10
|
+
def self.from_response(client, path, xml)
|
11
|
+
attrs = extract_attributes(xml)
|
12
|
+
new(client, path, attrs)
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.from_collection_response(client, path, xml)
|
16
|
+
xml.xpath("//xmlns:schedules/xmlns:schedule").each do |s|
|
17
|
+
id = s.xpath("//xmlns:schedule/@id")[0].value
|
18
|
+
yield from_response(client, path, s)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'tableau-server-client/resources/resource'
|
2
|
+
require 'tableau-server-client/resources/datasource'
|
3
|
+
require 'tableau-server-client/resources/workbook'
|
4
|
+
|
5
|
+
module TableauServerClient
|
6
|
+
module Resources
|
7
|
+
|
8
|
+
class Site < Resource
|
9
|
+
|
10
|
+
attr_reader :id, :name, :content_url, :admin_mode, :storage_quota, :state
|
11
|
+
|
12
|
+
def self.from_response(client, path, xml)
|
13
|
+
attrs = extract_attributes(xml)
|
14
|
+
new(client, path, attrs)
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.from_collection_response(client, path, xml)
|
18
|
+
xml.xpath("//xmlns:sites/xmlns:site").each do |s|
|
19
|
+
id = s['id']
|
20
|
+
yield from_response(client, "#{path}/#{id}", s)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def datasources(filter: [])
|
25
|
+
@client.get_collection Datasource.location(path, filter: filter)
|
26
|
+
end
|
27
|
+
|
28
|
+
def datasource(id)
|
29
|
+
@client.get Datasource.location(path, id)
|
30
|
+
end
|
31
|
+
|
32
|
+
def workbooks(filter: [])
|
33
|
+
@client.get_collection Workbook.location(path, filter: filter)
|
34
|
+
end
|
35
|
+
|
36
|
+
def workbook(id)
|
37
|
+
@client.get Workbook.location(path, id)
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'tableau-server-client/resources/resource'
|
2
|
+
require 'tableau-server-client/resources/project'
|
3
|
+
require 'tableau-server-client/resources/connection'
|
4
|
+
|
5
|
+
module TableauServerClient
|
6
|
+
module Resources
|
7
|
+
|
8
|
+
class Workbook < Resource
|
9
|
+
|
10
|
+
attr_reader :id, :name, :content_url, :show_tabs, :size, :created_at, :updated_at, :project, :owner
|
11
|
+
|
12
|
+
def self.from_response(client, path, xml)
|
13
|
+
attrs = extract_attributes(xml)
|
14
|
+
project = xml.xpath("xmlns:project")[0]
|
15
|
+
site_path = extract_site_path(path)
|
16
|
+
project_path = "#{site_path}/projects/#{project['id']}"
|
17
|
+
attrs['project'] = Project.from_response(client, project_path, project)
|
18
|
+
#TODO add owner
|
19
|
+
new(client, path, attrs)
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.from_collection_response(client, path, xml)
|
23
|
+
xml.xpath("//xmlns:workbooks/xmlns:workbook").each do |s|
|
24
|
+
id = s['id']
|
25
|
+
yield from_response(client, "#{path}/#{id}", s)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def connections
|
30
|
+
@client.get_collection Connection.location(path)
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'tableau-server-client/client'
|
2
|
+
require 'tableau-server-client/resources/site'
|
3
|
+
require 'tableau-server-client/resources/schedule'
|
4
|
+
require 'logger'
|
5
|
+
|
6
|
+
module TableauServerClient
|
7
|
+
class Server
|
8
|
+
|
9
|
+
def initialize(server_url, username, password,
|
10
|
+
site_name: "default", api_version: "2.8", token_lifetime: 240)
|
11
|
+
@logger = ::Logger.new(STDOUT)
|
12
|
+
@logger.level = ::Logger::INFO
|
13
|
+
@client = Client.new(server_url, username, password, site_name, api_version, token_lifetime, @logger)
|
14
|
+
end
|
15
|
+
|
16
|
+
def sites
|
17
|
+
client.get_collection Resources::Site.location(path)
|
18
|
+
end
|
19
|
+
|
20
|
+
def site(id)
|
21
|
+
client.get Resources::Site.location(path, id)
|
22
|
+
end
|
23
|
+
|
24
|
+
def schedules
|
25
|
+
client.get_collection Resources::Schedule.location(path)
|
26
|
+
end
|
27
|
+
|
28
|
+
def path
|
29
|
+
nil
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
attr_reader :client
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
|
3
|
+
module TableauServerClient
|
4
|
+
|
5
|
+
class Token
|
6
|
+
|
7
|
+
def initialize(site_id, user_id, token, lifetime, created_at = Time.now)
|
8
|
+
@site_id = site_id
|
9
|
+
@user_id = user_id
|
10
|
+
@token = token
|
11
|
+
@lifetime = lifetime
|
12
|
+
@created_at = created_at
|
13
|
+
end
|
14
|
+
|
15
|
+
attr_reader :site_id, :user_id, :token
|
16
|
+
|
17
|
+
def self.parse(xml, lifetime)
|
18
|
+
cred = Nokogiri::XML(xml).xpath("//xmlns:credentials")
|
19
|
+
sid = cred.xpath("//xmlns:site")[0]['id']
|
20
|
+
uid = cred.xpath("//xmlns:user")[0]['id']
|
21
|
+
new(sid, uid, cred[0]['token'], lifetime)
|
22
|
+
end
|
23
|
+
|
24
|
+
SAFETY_BUFFER_MIN = 10
|
25
|
+
|
26
|
+
def valid?
|
27
|
+
Time.now < @created_at + (@lifetime + SAFETY_BUFFER_MIN) * 60
|
28
|
+
end
|
29
|
+
|
30
|
+
def to_s
|
31
|
+
@token
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'tableau-server-client/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "tableau_server_client"
|
8
|
+
spec.version = TableauServerClient::VERSION
|
9
|
+
spec.authors = ["shimpeko"]
|
10
|
+
spec.email = ["shimpeko@swimmingpython.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{Tableau Server REST API Client}
|
13
|
+
spec.description = %q{REST API Client for Tableau Server.}
|
14
|
+
spec.homepage = "https://github.com/shimpeko/tableau-server-client"
|
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 = "bin"
|
21
|
+
spec.executables = ["console"]
|
22
|
+
spec.require_paths = ["lib"]
|
23
|
+
|
24
|
+
spec.add_development_dependency "bundler", "~> 1.13"
|
25
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
26
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
27
|
+
spec.add_development_dependency "byebug"
|
28
|
+
|
29
|
+
spec.add_dependency 'faraday', '>= 0.14.0'
|
30
|
+
spec.add_dependency 'nokogiri', '>= 1.8.2'
|
31
|
+
spec.add_dependency 'pry', '>= 0.11.3'
|
32
|
+
end
|
metadata
ADDED
@@ -0,0 +1,163 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: tableau_server_client
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.3
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- shimpeko
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-04-12 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.13'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.13'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: byebug
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: faraday
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 0.14.0
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 0.14.0
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: nokogiri
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 1.8.2
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 1.8.2
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: pry
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: 0.11.3
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 0.11.3
|
111
|
+
description: REST API Client for Tableau Server.
|
112
|
+
email:
|
113
|
+
- shimpeko@swimmingpython.com
|
114
|
+
executables:
|
115
|
+
- console
|
116
|
+
extensions: []
|
117
|
+
extra_rdoc_files: []
|
118
|
+
files:
|
119
|
+
- Gemfile
|
120
|
+
- Gemfile.lock
|
121
|
+
- README.md
|
122
|
+
- Rakefile
|
123
|
+
- bin/console
|
124
|
+
- lib/tableau-server-client/client.rb
|
125
|
+
- lib/tableau-server-client/paginatable_response.rb
|
126
|
+
- lib/tableau-server-client/request_builder.rb
|
127
|
+
- lib/tableau-server-client/request_url.rb
|
128
|
+
- lib/tableau-server-client/resources/connection.rb
|
129
|
+
- lib/tableau-server-client/resources/datasource.rb
|
130
|
+
- lib/tableau-server-client/resources/project.rb
|
131
|
+
- lib/tableau-server-client/resources/resource.rb
|
132
|
+
- lib/tableau-server-client/resources/schedule.rb
|
133
|
+
- lib/tableau-server-client/resources/site.rb
|
134
|
+
- lib/tableau-server-client/resources/workbook.rb
|
135
|
+
- lib/tableau-server-client/server.rb
|
136
|
+
- lib/tableau-server-client/token.rb
|
137
|
+
- lib/tableau-server-client/version.rb
|
138
|
+
- tableau_server_client.gemspec
|
139
|
+
homepage: https://github.com/shimpeko/tableau-server-client
|
140
|
+
licenses:
|
141
|
+
- MIT
|
142
|
+
metadata: {}
|
143
|
+
post_install_message:
|
144
|
+
rdoc_options: []
|
145
|
+
require_paths:
|
146
|
+
- lib
|
147
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
148
|
+
requirements:
|
149
|
+
- - ">="
|
150
|
+
- !ruby/object:Gem::Version
|
151
|
+
version: '0'
|
152
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
153
|
+
requirements:
|
154
|
+
- - ">="
|
155
|
+
- !ruby/object:Gem::Version
|
156
|
+
version: '0'
|
157
|
+
requirements: []
|
158
|
+
rubyforge_project:
|
159
|
+
rubygems_version: 2.6.8
|
160
|
+
signing_key:
|
161
|
+
specification_version: 4
|
162
|
+
summary: Tableau Server REST API Client
|
163
|
+
test_files: []
|