gooddata 2.1.8-java → 2.1.9-java
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/.rubocop.yml +6 -0
- data/.travis.yml +1 -1
- data/Dockerfile +9 -4
- data/Dockerfile.jruby +4 -4
- data/Dockerfile.ruby +5 -4
- data/SDK_VERSION +1 -1
- data/VERSION +1 -1
- data/bin/provision.sh +2 -0
- data/bin/release.sh +2 -0
- data/bin/rollout.sh +2 -0
- data/bin/run_brick.rb +28 -7
- data/bin/test_projects_cleanup.rb +4 -0
- data/bin/user_filters.sh +2 -0
- data/ci.rake +1 -1
- data/dev-gooddata-sso.pub.encrypted +40 -40
- data/gooddata.gemspec +5 -1
- data/lcm.rake +10 -0
- data/lib/gooddata/bricks/middleware/execution_result_middleware.rb +68 -0
- data/lib/gooddata/bricks/middleware/logger_middleware.rb +2 -1
- data/lib/gooddata/bricks/middleware/mask_logger_decorator.rb +5 -1
- data/lib/gooddata/bricks/pipeline.rb +7 -0
- data/lib/gooddata/cloud_resources/cloud_resouce_factory.rb +28 -0
- data/lib/gooddata/cloud_resources/cloud_resource_client.rb +24 -0
- data/lib/gooddata/cloud_resources/cloud_resources.rb +12 -0
- data/lib/gooddata/cloud_resources/redshift/drivers/log4j.properties +15 -0
- data/lib/gooddata/cloud_resources/redshift/redshift_client.rb +100 -0
- data/lib/gooddata/exceptions/invalid_env_error.rb +15 -0
- data/lib/gooddata/helpers/data_helper.rb +10 -0
- data/lib/gooddata/helpers/global_helpers.rb +4 -0
- data/lib/gooddata/helpers/global_helpers_params.rb +4 -7
- data/lib/gooddata/lcm/actions/collect_segment_clients.rb +4 -1
- data/lib/gooddata/lcm/actions/collect_segments.rb +1 -2
- data/lib/gooddata/lcm/actions/create_segment_masters.rb +5 -3
- data/lib/gooddata/lcm/actions/synchronize_clients.rb +1 -1
- data/lib/gooddata/lcm/actions/synchronize_etls_in_segment.rb +1 -2
- data/lib/gooddata/lcm/actions/synchronize_ldm.rb +10 -2
- data/lib/gooddata/lcm/actions/synchronize_user_filters.rb +22 -2
- data/lib/gooddata/lcm/actions/synchronize_users.rb +19 -0
- data/lib/gooddata/lcm/actions/update_release_table.rb +7 -1
- data/lib/gooddata/lcm/exceptions/lcm_execution_error.rb +16 -0
- data/lib/gooddata/lcm/helpers/release_table_helper.rb +16 -8
- data/lib/gooddata/lcm/lcm2.rb +6 -4
- data/lib/gooddata/models/execution.rb +0 -1
- data/lib/gooddata/models/execution_detail.rb +0 -1
- data/lib/gooddata/models/profile.rb +33 -11
- data/lib/gooddata/models/project.rb +2 -2
- data/lib/gooddata/models/project_creator.rb +2 -0
- data/lib/gooddata/models/schedule.rb +0 -1
- data/lib/gooddata/rest/client.rb +2 -2
- data/lib/gooddata/rest/connection.rb +5 -3
- data/rubydev_public.gpg.encrypted +51 -51
- data/rubydev_secret_keys.gpg.encrypted +109 -109
- metadata +12 -5
- data/lib/gooddata/extensions/hash.rb +0 -18
@@ -190,6 +190,9 @@ module GoodData
|
|
190
190
|
create_non_existing_user_groups: create_non_existing_user_groups,
|
191
191
|
user_groups_cache: nil
|
192
192
|
}
|
193
|
+
GoodData.gd_logger.info("Synchronizing in mode=#{mode}, data_rows=#{new_users.size}")
|
194
|
+
|
195
|
+
GoodData.logger.info("Synchronizing in mode \"#{mode}\"")
|
193
196
|
results = case mode
|
194
197
|
when 'add_to_organization'
|
195
198
|
domain.create_users(new_users.uniq { |u| u[:login] || u[:email] })
|
@@ -197,6 +200,8 @@ module GoodData
|
|
197
200
|
user_ids = new_users.uniq { |u| u[:login] || u[:email] }.map { |u| u[:login] || u[:email] }
|
198
201
|
users = user_ids.map { |u| domain.users(u, client: client) }
|
199
202
|
params.gdc_logger.warn "Deleting #{users.count} users from domain #{domain_name}"
|
203
|
+
|
204
|
+
GoodData.gd_logger.info("Synchronizing in mode=#{mode}, domain=#{domain_name}, data_rows=#{users.count}")
|
200
205
|
users.map(&:delete)
|
201
206
|
when 'sync_project'
|
202
207
|
project.import_users(new_users, common_params)
|
@@ -204,6 +209,8 @@ module GoodData
|
|
204
209
|
new_users.group_by { |u| u[:pid] }.flat_map do |project_id, users|
|
205
210
|
begin
|
206
211
|
project = client.projects(project_id)
|
212
|
+
|
213
|
+
GoodData.gd_logger.info("Synchronizing in mode=#{mode}, project_id=#{project_id}, data_rows=#{users.count}")
|
207
214
|
project.import_users(users, common_params)
|
208
215
|
rescue RestClient::ResourceNotFound
|
209
216
|
fail "Project \"#{project_id}\" was not found. Please check your project ids in the source file"
|
@@ -215,6 +222,8 @@ module GoodData
|
|
215
222
|
end
|
216
223
|
when 'sync_one_project_based_on_pid'
|
217
224
|
filtered_users = new_users.select { |u| u[:pid] == project.pid }
|
225
|
+
|
226
|
+
GoodData.gd_logger.info("Synchronizing in mode=#{mode}, data_rows=#{filtered_users.count}")
|
218
227
|
project.import_users(filtered_users, common_params)
|
219
228
|
when 'sync_one_project_based_on_custom_id'
|
220
229
|
filter_value = UserBricksHelper.resolve_client_id(domain, project, data_product)
|
@@ -235,6 +244,7 @@ module GoodData
|
|
235
244
|
end
|
236
245
|
|
237
246
|
GoodData.logger.info("Project #{project.pid} will receive #{filtered_users.count} from #{new_users.count} users")
|
247
|
+
GoodData.gd_logger.info("Synchronizing in mode=#{mode}, project_id=#{project.pid}, filtered_users=#{filtered_users.count}, data_rows=#{new_users.count}")
|
238
248
|
project.import_users(filtered_users, common_params)
|
239
249
|
when 'sync_multiple_projects_based_on_custom_id'
|
240
250
|
all_clients = domain.clients(:all, data_product).to_a
|
@@ -248,6 +258,8 @@ module GoodData
|
|
248
258
|
fail "Client #{client_id} does not have project." unless project
|
249
259
|
|
250
260
|
GoodData.logger.info("Project #{project.pid} of client #{client_id} will receive #{users.count} users")
|
261
|
+
|
262
|
+
GoodData.gd_logger.info("Synchronizing in mode=#{mode}, project_id=#{project.pid}, data_rows=#{users.count}")
|
251
263
|
project.import_users(users, common_params)
|
252
264
|
end
|
253
265
|
when 'sync_domain_client_workspaces'
|
@@ -280,6 +292,8 @@ module GoodData
|
|
280
292
|
|
281
293
|
working_client_ids << client_id.to_s
|
282
294
|
GoodData.logger.info("Project #{project.pid} of client #{client_id} will receive #{users.count} users")
|
295
|
+
|
296
|
+
GoodData.gd_logger.info("Synchronizing in mode=#{mode}, project_id=#{project.pid}, data_rows=#{users.count}")
|
283
297
|
project.import_users(users, common_params)
|
284
298
|
end
|
285
299
|
|
@@ -303,13 +317,18 @@ module GoodData
|
|
303
317
|
next
|
304
318
|
end
|
305
319
|
GoodData.logger.info("Synchronizing all users in project #{project.pid} of client #{c.client_id}")
|
320
|
+
|
321
|
+
GoodData.gd_logger.info("Synchronizing all users in project_id=#{project.pid}, client_id=#{c.client_id}")
|
306
322
|
res += project.import_users([], common_params)
|
307
323
|
end
|
308
324
|
end
|
309
325
|
|
310
326
|
res
|
311
327
|
when 'sync_domain_and_project'
|
328
|
+
GoodData.gd_logger.info("Create users in mode=#{mode}, data_rows=#{new_users.count}")
|
312
329
|
domain.create_users(new_users, ignore_failures: ignore_failures)
|
330
|
+
|
331
|
+
GoodData.gd_logger.info("Import users in mode=#{mode}, data_rows=#{new_users.count}")
|
313
332
|
project.import_users(new_users, common_params)
|
314
333
|
end
|
315
334
|
|
@@ -39,6 +39,7 @@ module GoodData
|
|
39
39
|
class << self
|
40
40
|
def call(params)
|
41
41
|
client = params.gdc_gd_client
|
42
|
+
GoodData.gd_logger.info("Update release table: use_nfs=#{params.ads_client.nil?}")
|
42
43
|
|
43
44
|
domain_name = params.organization || params.domain
|
44
45
|
fail "Either organisation or domain has to be specified in params" unless domain_name
|
@@ -48,6 +49,7 @@ module GoodData
|
|
48
49
|
segment_id = segment_in.segment_id
|
49
50
|
|
50
51
|
placeholders = {
|
52
|
+
data_product_id: segment_in[:data_product_id],
|
51
53
|
segment_id: segment_in[:segment_id],
|
52
54
|
master_project_id: segment_in[:master_pid],
|
53
55
|
version: segment_in[:version],
|
@@ -83,7 +85,11 @@ module GoodData
|
|
83
85
|
|
84
86
|
params.ads_client.execute(query)
|
85
87
|
else
|
86
|
-
|
88
|
+
data_product_id = placeholders[:data_product_id]
|
89
|
+
segment_id = placeholders[:segment_id]
|
90
|
+
master_pid = placeholders[:master_project_id]
|
91
|
+
version = placeholders[:version]
|
92
|
+
GoodData::LCM2::Helpers.update_latest_master_to_nfs(domain_id, data_product_id, segment_id, master_pid, version)
|
87
93
|
end
|
88
94
|
end
|
89
95
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# Copyright (c) 2010-2019 GoodData Corporation. All rights reserved.
|
2
|
+
# This source code is licensed under the BSD-style license found in the
|
3
|
+
# LICENSE file in the root directory of this source tree.
|
4
|
+
|
5
|
+
module GoodData
|
6
|
+
class LcmExecutionError < RuntimeError
|
7
|
+
DEFAULT_MSG = 'Error during lcm execution'
|
8
|
+
|
9
|
+
attr_reader :summary_error
|
10
|
+
|
11
|
+
def initialize(summary_error, message = DEFAULT_MSG)
|
12
|
+
super(message)
|
13
|
+
@summary_error = summary_error
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -8,7 +8,7 @@ module GoodData
|
|
8
8
|
module LCM2
|
9
9
|
class Helpers
|
10
10
|
DEFAULT_TABLE_NAME = 'LCM_RELEASE'
|
11
|
-
DEFAULT_NFS_DIRECTORY = 'release-tables'
|
11
|
+
DEFAULT_NFS_DIRECTORY = '/release-tables'
|
12
12
|
|
13
13
|
class << self
|
14
14
|
def latest_master_project_from_ads(release_table_name, ads_client, segment_id)
|
@@ -25,22 +25,30 @@ module GoodData
|
|
25
25
|
sorted.last
|
26
26
|
end
|
27
27
|
|
28
|
-
def latest_master_project_from_nfs(domain_id, segment_id)
|
29
|
-
|
30
|
-
data
|
28
|
+
def latest_master_project_from_nfs(domain_id, data_product_id, segment_id)
|
29
|
+
file_path = path_to_release_table_file(domain_id, data_product_id, segment_id)
|
30
|
+
data = GoodData::Helpers::Csv.read_as_hash(file_path)
|
31
|
+
latest_master_project = data.sort_by { |master| master[:version] }
|
31
32
|
.reverse.first
|
33
|
+
|
34
|
+
version_info = latest_master_project ? "master_pid=#{latest_master_project[:master_project_id]} version=#{latest_master_project[:version]}" : ""
|
35
|
+
GoodData.gd_logger.info "Getting latest master project: file=#{file_path} domain=#{domain_id} data_product=#{data_product_id} segment=#{segment_id} #{version_info}"
|
36
|
+
latest_master_project
|
32
37
|
end
|
33
38
|
|
34
|
-
def update_latest_master_to_nfs(domain_id, segment_id, master_pid, version)
|
39
|
+
def update_latest_master_to_nfs(domain_id, data_product_id, segment_id, master_pid, version)
|
40
|
+
file_path = path_to_release_table_file(domain_id, data_product_id, segment_id)
|
41
|
+
GoodData.gd_logger.info "Updating release table: file=#{file_path} domain=#{domain_id} data_product=#{data_product_id} segment=#{segment_id} master_pid=#{master_pid} version=#{version}" # rubocop:disable Metrics/LineLength
|
35
42
|
GoodData::Helpers::Csv.ammend_line(
|
36
|
-
|
43
|
+
file_path,
|
37
44
|
master_project_id: master_pid,
|
38
45
|
version: version
|
39
46
|
)
|
40
47
|
end
|
41
48
|
|
42
|
-
def path_to_release_table_file(domain_id, segment_id)
|
43
|
-
|
49
|
+
def path_to_release_table_file(domain_id, data_prod_id, segment_id)
|
50
|
+
nsf_directory = ENV['RELEASE_TABLE_NFS_DIRECTORY'] || DEFAULT_NFS_DIRECTORY
|
51
|
+
[nsf_directory, domain_id, data_prod_id + '-' + segment_id + '.csv'].join('/')
|
44
52
|
end
|
45
53
|
end
|
46
54
|
end
|
data/lib/gooddata/lcm/lcm2.rb
CHANGED
@@ -18,6 +18,7 @@ require 'active_support/core_ext/hash/compact'
|
|
18
18
|
require_relative 'actions/actions'
|
19
19
|
require_relative 'dsl/dsl'
|
20
20
|
require_relative 'helpers/helpers'
|
21
|
+
require_relative 'exceptions/lcm_execution_error'
|
21
22
|
|
22
23
|
using TrueExtensions
|
23
24
|
using FalseExtensions
|
@@ -357,10 +358,11 @@ module GoodData
|
|
357
358
|
|
358
359
|
if errors.any?
|
359
360
|
error_message = JSON.pretty_generate(errors)
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
361
|
+
if strict_mode
|
362
|
+
raise GoodData::LcmExecutionError.new(errors[0][:err], error_message)
|
363
|
+
else
|
364
|
+
GoodData.logger.error(error_message)
|
365
|
+
end
|
364
366
|
end
|
365
367
|
|
366
368
|
result
|
@@ -324,20 +324,42 @@ module GoodData
|
|
324
324
|
end
|
325
325
|
|
326
326
|
# Gets the array of projects
|
327
|
-
#
|
327
|
+
# @param limit [Integer] maximum number of projects to get.
|
328
|
+
# @param offset [Integer] offset of the first project, start from 0.
|
328
329
|
# @return [Array<GoodData::Project>] Array of project where account settings belongs to
|
329
|
-
def projects(limit = nil)
|
330
|
+
def projects(limit = nil, offset = nil)
|
330
331
|
url = @json['accountSetting']['links']['projects']
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
332
|
+
|
333
|
+
all_projects = []
|
334
|
+
|
335
|
+
raise ArgumentError, 'Params limit and offset are expected' if !offset.nil? && limit.nil?
|
336
|
+
|
337
|
+
if limit.nil?
|
338
|
+
url += "?limit=500"
|
339
|
+
loop do
|
340
|
+
projects = client.get url
|
341
|
+
projects['projects']['items'].each do |project|
|
342
|
+
all_projects << client.create(GoodData::Project, project)
|
343
|
+
end
|
344
|
+
if !projects['projects']['paging'].nil? && !projects['projects']['paging']['next'].nil?
|
345
|
+
url = projects['projects']['paging']['next']
|
346
|
+
else
|
347
|
+
break
|
348
|
+
end
|
349
|
+
end
|
350
|
+
else
|
351
|
+
limit = [limit, 500].min if limit.is_a?(Integer) && limit > 0
|
352
|
+
|
353
|
+
url += "?limit=#{limit}"
|
354
|
+
url += "&offset=#{offset}" if !offset.nil? && offset.is_a?(Integer) && offset > 0
|
355
|
+
|
356
|
+
projects = client.get url
|
357
|
+
projects['projects']['items'].each do |project|
|
358
|
+
all_projects << client.create(GoodData::Project, project)
|
359
|
+
end
|
340
360
|
end
|
361
|
+
|
362
|
+
all_projects
|
341
363
|
end
|
342
364
|
|
343
365
|
# Saves object if dirty, clears dirty flag
|
@@ -67,9 +67,9 @@ module GoodData
|
|
67
67
|
class << self
|
68
68
|
# Returns an array of all projects accessible by
|
69
69
|
# current user
|
70
|
-
def all(opts = { client: GoodData.connection }, limit = nil)
|
70
|
+
def all(opts = { client: GoodData.connection }, limit = nil, offset = nil)
|
71
71
|
c = GoodData.get_client(opts)
|
72
|
-
c.user.projects(limit)
|
72
|
+
c.user.projects(limit, offset)
|
73
73
|
end
|
74
74
|
|
75
75
|
# Returns a Project object identified by given string
|
@@ -47,6 +47,8 @@ module GoodData
|
|
47
47
|
unless response
|
48
48
|
maql_diff_params = [:includeGrain]
|
49
49
|
maql_diff_params << :excludeFactRule if opts[:exclude_fact_rule]
|
50
|
+
maql_diff_params << :includeDeprecated if opts[:include_deprecated]
|
51
|
+
|
50
52
|
maql_diff_time = Benchmark.realtime do
|
51
53
|
response = project.maql_diff(blueprint: bp, params: maql_diff_params)
|
52
54
|
end
|
data/lib/gooddata/rest/client.rb
CHANGED
@@ -188,11 +188,11 @@ module GoodData
|
|
188
188
|
end
|
189
189
|
end
|
190
190
|
|
191
|
-
def projects(id = :all, limit = nil)
|
191
|
+
def projects(id = :all, limit = nil, offset = nil)
|
192
192
|
if limit.nil?
|
193
193
|
GoodData::Project[id, client: self]
|
194
194
|
else
|
195
|
-
GoodData::Project.all({ client: self }, limit)
|
195
|
+
GoodData::Project.all({ client: self }, limit, offset)
|
196
196
|
end
|
197
197
|
end
|
198
198
|
|
@@ -30,6 +30,7 @@ module GoodData
|
|
30
30
|
LOGIN_PATH = '/gdc/account/login'
|
31
31
|
TOKEN_PATH = '/gdc/account/token'
|
32
32
|
KEYS_TO_SCRUB = [:password, :verifyPassword, :authorizationToken]
|
33
|
+
API_LEVEL = 2
|
33
34
|
|
34
35
|
ID_LENGTH = 16
|
35
36
|
|
@@ -307,7 +308,6 @@ module GoodData
|
|
307
308
|
# Remove when TT sent in headers. Currently we need to parse from body
|
308
309
|
merge_headers!(:x_gdc_authtt => GoodData::Helpers.get_path(response, %w(userToken token)))
|
309
310
|
rescue Exception => e # rubocop:disable RescueException
|
310
|
-
GoodData.logger.error(e.message)
|
311
311
|
raise e
|
312
312
|
end
|
313
313
|
end
|
@@ -350,6 +350,9 @@ module GoodData
|
|
350
350
|
profile method.to_s.upcase, uri, request_id, stats_on do
|
351
351
|
b = proc do
|
352
352
|
params = fresh_request_params(request_id).merge(options)
|
353
|
+
|
354
|
+
params['X-GDC-VERSION'] = API_LEVEL
|
355
|
+
|
353
356
|
case method
|
354
357
|
when :get
|
355
358
|
@server[uri].get(params, &user_block)
|
@@ -514,8 +517,7 @@ module GoodData
|
|
514
517
|
begin
|
515
518
|
request.execute
|
516
519
|
rescue => e
|
517
|
-
|
518
|
-
raise e
|
520
|
+
raise "Error when uploading file #{filename}. Error: #{e}"
|
519
521
|
end
|
520
522
|
end
|
521
523
|
|
@@ -1,51 +1,51 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
1
|
+
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
|