tableau_server_client 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|