gooddata 0.6.15 → 0.6.16
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 +4 -4
- data/lib/gooddata/bricks/middleware/fs_download_middleware.rb +42 -0
- data/lib/gooddata/bricks/middleware/fs_upload_middleware.rb +8 -12
- data/lib/gooddata/bricks/middleware/gooddata_middleware.rb +10 -13
- data/lib/gooddata/bricks/middleware/logger_middleware.rb +7 -2
- data/lib/gooddata/core/rest.rb +7 -8
- data/lib/gooddata/helpers/data_helper.rb +81 -0
- data/lib/gooddata/helpers/global_helpers.rb +1 -1
- data/lib/gooddata/models/domain.rb +22 -48
- data/lib/gooddata/models/membership.rb +2 -0
- data/lib/gooddata/models/metadata/attribute.rb +1 -1
- data/lib/gooddata/models/metadata/dataset.rb +1 -1
- data/lib/gooddata/models/process.rb +3 -2
- data/lib/gooddata/models/profile.rb +8 -4
- data/lib/gooddata/models/project.rb +128 -80
- data/lib/gooddata/models/project_role.rb +26 -0
- data/lib/gooddata/models/user_filters/user_filter_builder.rb +10 -9
- data/lib/gooddata/rest/client.rb +2 -4
- data/lib/gooddata/rest/connection.rb +3 -2
- data/lib/gooddata/version.rb +1 -1
- data/spec/helpers/project_helper.rb +1 -1
- data/spec/integration/project_spec.rb +10 -5
- data/spec/integration/rest_spec.rb +4 -4
- data/spec/integration/user_filters_spec.rb +2 -1
- data/spec/unit/models/metric_spec.rb +9 -9
- data/spec/unit/models/project_spec.rb +0 -272
- data/spec/unit/models/unit_project.rb +122 -0
- metadata +4 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 40814e8c9d7deee9ac0eca814a3ac3c635f1ede3
|
4
|
+
data.tar.gz: fe14bb4f38e410de2e7abc9624e5f3aba6c39e6f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f4128f12fab224bc8a5814fe0dcee35c385c2652defe34afb331562a65332b7f4fdd9199b7bb4c653eebdff39d6dd21950482e10fb1b51c5e81664072c490035
|
7
|
+
data.tar.gz: 731f9c3dab1e859777931f44362e4e50c2bc79fa562a4172ad2d53c658d9b3f309ff2000fb36a9090bdecd09be5240d76d3344316d60b281803bcc3d4e90373e
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require_relative 'base_middleware'
|
3
|
+
|
4
|
+
module GoodData
|
5
|
+
module Bricks
|
6
|
+
class FsProjectDownloadMiddleware < Bricks::Middleware
|
7
|
+
def call(params)
|
8
|
+
(params['gdc_files_to_download'] || []).each do |source|
|
9
|
+
case source[:type].to_s
|
10
|
+
when 'ads'
|
11
|
+
CSV.open(source[:to], 'w') do |csv|
|
12
|
+
header_written = false
|
13
|
+
header = nil
|
14
|
+
dwh = params['ads_client']
|
15
|
+
dwh.execute_select(source[:query]) do |row|
|
16
|
+
unless header_written
|
17
|
+
header_written = true
|
18
|
+
header = row.keys
|
19
|
+
csv << header
|
20
|
+
end
|
21
|
+
csv << row.values_at(*header)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
when 'staging'
|
25
|
+
webdav_uri = GoodData.project_webdav_path
|
26
|
+
dav = Net::DAV.new(webdav_uri, :curl => false)
|
27
|
+
dav.verify_server = false
|
28
|
+
dav.credentials(params['GDC_USERNAME'], params['GDC_PASSWORD'])
|
29
|
+
dav.find(path, recursive: true, suppress_errors: true) do |item|
|
30
|
+
puts 'Checking: ' + item.url.to_s
|
31
|
+
name = (item.uri - webdav_uri).to_s
|
32
|
+
File.open(name, 'w') do |f|
|
33
|
+
f << item.content
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
@app.call(params)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -1,9 +1,4 @@
|
|
1
1
|
# encoding: UTF-8
|
2
|
-
|
3
|
-
require 'uri'
|
4
|
-
require 'net/http'
|
5
|
-
require 'pathname'
|
6
|
-
|
7
2
|
require_relative 'base_middleware'
|
8
3
|
|
9
4
|
module GoodData
|
@@ -15,20 +10,21 @@ module GoodData
|
|
15
10
|
end
|
16
11
|
|
17
12
|
def call(params)
|
18
|
-
returning(@app.call(params)) do |
|
13
|
+
returning(@app.call(params)) do |_|
|
19
14
|
destination = @destination
|
20
15
|
(params['gdc_files_to_upload'] || []).each do |f|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
16
|
+
path = f[:path]
|
17
|
+
case destination.to_sym
|
18
|
+
when :staging
|
19
|
+
GoodData.client.get '/gdc/account/token', :dont_reauth => true
|
20
|
+
url = GoodData.project_webdav_path
|
21
|
+
GoodData.upload_to_project_webdav(path)
|
22
|
+
puts "Uploaded local file \"#{path}\" to url \"#{url + path}\""
|
25
23
|
end
|
26
24
|
end
|
27
25
|
end
|
28
26
|
end
|
29
27
|
end
|
30
|
-
|
31
|
-
# Alias to make it backwards compatible
|
32
28
|
FsUploadMiddleware = FsProjectUploadMiddleware
|
33
29
|
end
|
34
30
|
end
|
@@ -1,8 +1,4 @@
|
|
1
1
|
# encoding: UTF-8
|
2
|
-
|
3
|
-
# TODO: REmove this
|
4
|
-
# require 'gooddata'
|
5
|
-
|
6
2
|
require_relative 'base_middleware'
|
7
3
|
|
8
4
|
module GoodData
|
@@ -11,25 +7,26 @@ module GoodData
|
|
11
7
|
def call(params)
|
12
8
|
logger = params['GDC_LOGGER']
|
13
9
|
token_name = 'GDC_SST'
|
14
|
-
protocol_name = '
|
15
|
-
server_name = '
|
10
|
+
protocol_name = 'CLIENT_GDC_PROTOCOL'
|
11
|
+
server_name = 'CLIENT_GDC_HOSTNAME'
|
16
12
|
project_id = params['GDC_PROJECT_ID']
|
17
13
|
|
18
|
-
|
19
|
-
|
20
|
-
server = if !params[protocol_name].empty? && !params[server_name].empty?
|
14
|
+
server = if params[protocol_name] && params[server_name]
|
21
15
|
"#{params[protocol_name]}://#{params[server_name]}"
|
22
16
|
end
|
23
17
|
|
24
18
|
client = if params['GDC_USERNAME'].nil? || params['GDC_PASSWORD'].nil?
|
25
|
-
|
19
|
+
puts "Connecting with SST to server #{server}"
|
20
|
+
fail 'SST (SuperSecureToken) not present in params' if params[token_name].nil?
|
21
|
+
GoodData.connect(sst_token: params[token_name], server: server)
|
26
22
|
else
|
23
|
+
puts "Connecting as #{params['GDC_USERNAME']} to server #{server}"
|
27
24
|
GoodData.connect(params['GDC_USERNAME'], params['GDC_PASSWORD'], server: server)
|
28
25
|
end
|
26
|
+
project = client.projects(project_id)
|
27
|
+
GoodData.project = project
|
29
28
|
GoodData.logger = logger
|
30
|
-
|
31
|
-
@app.call(params)
|
32
|
-
end
|
29
|
+
@app.call(params.merge('GDC_GD_CLIENT' => client, 'gdc_project' => project))
|
33
30
|
end
|
34
31
|
end
|
35
32
|
end
|
@@ -8,8 +8,13 @@ module GoodData
|
|
8
8
|
module Bricks
|
9
9
|
class LoggerMiddleware < Bricks::Middleware
|
10
10
|
def call(params)
|
11
|
-
logger =
|
12
|
-
|
11
|
+
logger = nil
|
12
|
+
if params['GDC_LOGGING_OFF']
|
13
|
+
logger = NilLogger.new
|
14
|
+
else
|
15
|
+
logger = params['GDC_LOGGER'] = params[:GDC_LOGGER_FILE].nil? ? Logger.new(STDOUT) : Logger.new(params[:GDC_LOGGER_FILE])
|
16
|
+
logger.info('Pipeline starts')
|
17
|
+
end
|
13
18
|
returning(@app.call(params)) do |_result|
|
14
19
|
logger.info('Pipeline ending')
|
15
20
|
end
|
data/lib/gooddata/core/rest.rb
CHANGED
@@ -77,39 +77,38 @@ module GoodData
|
|
77
77
|
|
78
78
|
# Get WebDav directory for project data
|
79
79
|
# @return [String]
|
80
|
-
def
|
80
|
+
def project_webdav_path(options = { :project => GoodData.project })
|
81
81
|
options = merge_options(options)
|
82
82
|
project = options[:project]
|
83
|
-
project.
|
83
|
+
project.project_webdav_path
|
84
84
|
end
|
85
85
|
|
86
86
|
# Upload to project directory
|
87
87
|
def upload_to_project_webdav(file, options = { :project => GoodData.project })
|
88
88
|
options = merge_options(options)
|
89
|
-
|
90
|
-
url = get_project_webdav_path(webdav_filename, options)
|
89
|
+
url = project_webdav_path(options)
|
91
90
|
connection.upload(file, options.merge(:staging_url => url))
|
92
91
|
end
|
93
92
|
|
94
93
|
# Download from project directory
|
95
94
|
def download_from_project_webdav(file, where, options = { :project => GoodData.project })
|
96
95
|
options = merge_options(options)
|
97
|
-
url =
|
96
|
+
url = project_webdav_path(options)
|
98
97
|
connection.download(file, where, options.merge(:staging_url => url))
|
99
98
|
end
|
100
99
|
|
101
100
|
# Get WebDav directory for user data
|
102
101
|
# @return [String]
|
103
|
-
def
|
102
|
+
def user_webdav_path(options = { :project => GoodData.project })
|
104
103
|
options = merge_options(options)
|
105
104
|
project = options[:project]
|
106
|
-
project.
|
105
|
+
project.user_webdav_path
|
107
106
|
end
|
108
107
|
|
109
108
|
# Download from user directory
|
110
109
|
def download_from_user_webdav(file, where, options = { :project => GoodData.project })
|
111
110
|
options = merge_options(options)
|
112
|
-
url =
|
111
|
+
url = user_webdav_path(options)
|
113
112
|
connection.download(file, where, options.merge(:staging_url => url))
|
114
113
|
end
|
115
114
|
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require 'csv'
|
4
|
+
require 'digest'
|
5
|
+
require 'open-uri'
|
6
|
+
|
7
|
+
module GoodData
|
8
|
+
module Helpers
|
9
|
+
class DataSource
|
10
|
+
attr_reader :realized
|
11
|
+
|
12
|
+
def initialize(opts = {})
|
13
|
+
opts = opts.is_a?(String) ? { type: :staging, path: opts } : opts
|
14
|
+
opts = opts.symbolize_keys
|
15
|
+
@source = opts[:type]
|
16
|
+
@options = opts
|
17
|
+
@realized = false
|
18
|
+
end
|
19
|
+
|
20
|
+
def realize_query(params)
|
21
|
+
query = @options[:query]
|
22
|
+
dwh = params['ads_client']
|
23
|
+
fail "Data Source needs a client to ads to be able to query the storage but 'ads_client' is empty." unless dwh
|
24
|
+
filename = Digest::SHA256.new.hexdigest(query)
|
25
|
+
measure = Benchmark.measure do
|
26
|
+
CSV.open(filename, 'w') do |csv|
|
27
|
+
header_written = false
|
28
|
+
header = nil
|
29
|
+
|
30
|
+
dwh.execute_select(query) do |row|
|
31
|
+
unless header_written
|
32
|
+
header_written = true
|
33
|
+
header = row.keys
|
34
|
+
csv << header
|
35
|
+
end
|
36
|
+
csv << row.values_at(*header)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
puts "Realizing SQL query \"#{query}\" took #{measure.real}"
|
41
|
+
filename
|
42
|
+
end
|
43
|
+
|
44
|
+
def realize_staging(params)
|
45
|
+
path = @options[:path]
|
46
|
+
url = URI.parse(path)
|
47
|
+
filename = Digest::SHA256.new.hexdigest(path)
|
48
|
+
if url.relative?
|
49
|
+
params['gdc_project'].download_file(path, filename)
|
50
|
+
else
|
51
|
+
params['GDC_GD_CLIENT'].download_file(path, filename)
|
52
|
+
end
|
53
|
+
filename
|
54
|
+
end
|
55
|
+
|
56
|
+
def realize_link
|
57
|
+
link = @options[:url]
|
58
|
+
filename = Digest::SHA256.new.hexdigest(link)
|
59
|
+
measure = Benchmark.measure do
|
60
|
+
File.open(filename, 'w') do |f|
|
61
|
+
open(link) { |rf| f.write(rf.read) }
|
62
|
+
end
|
63
|
+
end
|
64
|
+
puts "Realizing web download from \"#{link}\" took #{measure.real}"
|
65
|
+
filename
|
66
|
+
end
|
67
|
+
|
68
|
+
def realize(params = {})
|
69
|
+
@realized = true
|
70
|
+
case @source.to_s
|
71
|
+
when 'ads'
|
72
|
+
realize_query(params)
|
73
|
+
when 'staging'
|
74
|
+
realize_staging(params)
|
75
|
+
when 'web'
|
76
|
+
realize_link
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -10,8 +10,6 @@ module GoodData
|
|
10
10
|
class Domain < GoodData::Rest::Object
|
11
11
|
attr_reader :name
|
12
12
|
|
13
|
-
USERS_OPTIONS = { :offset => 0, :limit => 10_000 }
|
14
|
-
|
15
13
|
class << self
|
16
14
|
# Looks for domain
|
17
15
|
#
|
@@ -182,21 +180,22 @@ module GoodData
|
|
182
180
|
# @option opts [Number] :offset The subject
|
183
181
|
# @option opts [Number] :limit From address
|
184
182
|
# TODO: Review opts[:limit] functionality
|
185
|
-
def users(domain, opts =
|
183
|
+
def users(domain, opts = {})
|
186
184
|
result = []
|
187
|
-
|
188
|
-
|
189
|
-
|
185
|
+
page_limit = opts[:page_limit] || 1000
|
186
|
+
limit = opts[:limit] || Float::INFINITY
|
187
|
+
offset = opts[:offset]
|
188
|
+
uri = "/gdc/account/domains/#{domain}/users?offset=#{offset}&limit=#{page_limit}"
|
190
189
|
loop do
|
191
|
-
break unless uri
|
192
190
|
tmp = client(opts).get(uri)
|
193
191
|
tmp['accountSettings']['items'].each do |account|
|
194
192
|
result << client(opts).create(GoodData::Profile, account)
|
195
193
|
end
|
196
|
-
break if
|
194
|
+
break if result.length >= limit
|
195
|
+
|
197
196
|
uri = tmp['accountSettings']['paging']['next']
|
197
|
+
break unless uri
|
198
198
|
end
|
199
|
-
|
200
199
|
result
|
201
200
|
end
|
202
201
|
|
@@ -205,51 +204,26 @@ module GoodData
|
|
205
204
|
# @param [String] default_domain_name Default domain name used when no specified in user
|
206
205
|
# @return [Array<GoodData::User>] List of users created
|
207
206
|
def create_users(list, default_domain = nil, opts = { :client => GoodData.connection, :project => GoodData.project })
|
208
|
-
client = client(opts)
|
209
207
|
default_domain_name = default_domain.respond_to?(:name) ? default_domain.name : default_domain
|
210
|
-
|
211
|
-
|
212
|
-
|
208
|
+
domain = client.domain(default_domain_name)
|
209
|
+
|
210
|
+
domain_users_cache = Hash[domain.users.map { |u| [u.login, u] }]
|
211
|
+
list.pmapcat do |user|
|
213
212
|
begin
|
214
213
|
user_data = user.to_hash
|
215
|
-
|
216
|
-
domain_name = user_data[:domain] || default_domain_name
|
217
|
-
|
218
|
-
# Lookup for domain in cache'
|
219
|
-
domain = domains[domain_name]
|
220
|
-
|
221
|
-
# Get domain info from REST, add to cache
|
222
|
-
if domain.nil?
|
223
|
-
domain = {
|
224
|
-
:domain => domain_obj,
|
225
|
-
:users => domain_obj.users
|
226
|
-
}
|
227
|
-
|
228
|
-
domain[:users_map] = Hash[domain[:users].map { |u| [u.login, u] }]
|
229
|
-
domains[domain_name] = domain
|
230
|
-
end
|
231
|
-
|
232
|
-
# Check if user exists in domain
|
233
|
-
domain_user = domain[:users_map][user_data[:login]]
|
234
|
-
|
235
|
-
# Create domain user if needed
|
214
|
+
domain_user = domain_users_cache[user_data[:login]]
|
236
215
|
if !domain_user
|
237
|
-
|
238
|
-
|
239
|
-
domain[:users] << domain_user
|
240
|
-
domain[:users_map][domain_user.login] = domain_user
|
241
|
-
{ type: :user_added_to_domain, user: domain_user }
|
216
|
+
added_user = domain.add_user(user_data, opts)
|
217
|
+
[{ type: :user_added_to_domain, user: added_user }]
|
242
218
|
else
|
243
|
-
|
244
|
-
diff = GoodData::Helpers.diff([domain_user.to_hash], [user_data], key: :login)
|
245
|
-
next if diff[:changed].empty?
|
246
|
-
|
247
|
-
|
248
|
-
domain[:users_map][domain_user.login] = domain_user
|
249
|
-
{ type: :user_changed_in_domain, user: domain_user }
|
219
|
+
fields_to_check = opts[:fields_to_check] || user_data.keys
|
220
|
+
diff = GoodData::Helpers.diff([domain_user.to_hash], [user_data], key: :login, fields: fields_to_check)
|
221
|
+
next [] if diff[:changed].empty?
|
222
|
+
updated_user = domain.update_user(domain_user.to_hash.merge(user_data.compact), opts)
|
223
|
+
[{ type: :user_changed_in_domain, user: updated_user }]
|
250
224
|
end
|
251
225
|
rescue RuntimeError => e
|
252
|
-
{ type: :error, reason: e }
|
226
|
+
[{ type: :error, reason: e }]
|
253
227
|
end
|
254
228
|
end
|
255
229
|
end
|
@@ -358,7 +332,7 @@ module GoodData
|
|
358
332
|
# domain = GoodData::Domain['gooddata-tomas-korcak']
|
359
333
|
# pp domain.users
|
360
334
|
#
|
361
|
-
def users(opts =
|
335
|
+
def users(opts = {})
|
362
336
|
GoodData::Domain.users(name, opts.merge(client: client))
|
363
337
|
end
|
364
338
|
|
@@ -50,9 +50,11 @@ module GoodData
|
|
50
50
|
# And following lines are even much more ugly hack
|
51
51
|
# 'authentication_modes' => ['sso', 'password']
|
52
52
|
},
|
53
|
+
'links' => {},
|
53
54
|
'meta' => {}
|
54
55
|
}
|
55
56
|
}
|
57
|
+
json['user']['links']['self'] = data[:uri] if data[:uri]
|
56
58
|
c.create(self, json)
|
57
59
|
end
|
58
60
|
|
@@ -123,7 +123,7 @@ module GoodData
|
|
123
123
|
# @param name [String] name used as a basis for regular expression
|
124
124
|
# @return [GoodData::Label]
|
125
125
|
def label_by_name(name)
|
126
|
-
labels.find { |label| label.title
|
126
|
+
labels.find { |label| label.title =~ /#{name}/ || label.identifier =~ /#{name}/ }
|
127
127
|
end
|
128
128
|
end
|
129
129
|
end
|
@@ -167,9 +167,10 @@ module GoodData
|
|
167
167
|
path
|
168
168
|
else
|
169
169
|
with_zip(opts) do |zipfile|
|
170
|
-
Dir[File.join(path, '**', '**')].reject { |f| files_to_exclude.include?(Pathname(path) + f) }
|
170
|
+
files_to_upload = Dir[File.join(path, '**', '**')].reject { |f| files_to_exclude.include?(Pathname(path) + f) }
|
171
|
+
puts "Uploading #{files_to_upload.count} files."
|
172
|
+
files_to_upload.each do |file|
|
171
173
|
file_pathname = Pathname.new(file)
|
172
|
-
puts "Including item #{file_pathname}"
|
173
174
|
file_relative_pathname = file_pathname.relative_path_from(Pathname.new(path))
|
174
175
|
zipfile.add(file_relative_pathname, file)
|
175
176
|
end
|