gooddata 2.3.1 → 2.3.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 +4 -4
- data/.github/workflows/build.yml +8 -14
- data/.github/workflows/check-extended.yaml +245 -0
- data/.github/workflows/check.yaml +190 -0
- data/.github/workflows/gate.yaml +200 -0
- data/.github/workflows/pre-merge.yml +22 -53
- data/.travis.yml +0 -35
- data/CHANGELOG.md +3 -0
- data/Dockerfile +3 -9
- data/Dockerfile.jruby +13 -1
- data/SDK_VERSION +1 -1
- data/VERSION +1 -1
- data/ci/bigquery/pom.xml +1 -1
- data/ci/postgresql/pom.xml +1 -1
- data/ci/snowflake/pom.xml +1 -1
- data/gooddata.gemspec +6 -2
- data/lib/gooddata/cloud_resources/mssql/mssql_client.rb +2 -1
- data/lib/gooddata/cloud_resources/mysql/mysql_client.rb +2 -1
- data/lib/gooddata/cloud_resources/postgresql/postgresql_client.rb +2 -1
- data/lib/gooddata/cloud_resources/redshift/redshift_client.rb +2 -1
- data/lib/gooddata/cloud_resources/snowflake/snowflake_client.rb +4 -1
- data/lib/gooddata/helpers/global_helpers.rb +1 -1
- data/lib/gooddata/lcm/actions/import_object_collections.rb +6 -2
- data/lib/gooddata/lcm/actions/synchronize_clients.rb +22 -2
- data/lib/gooddata/lcm/actions/synchronize_dataset_mappings.rb +6 -2
- data/lib/gooddata/lcm/actions/synchronize_user_filters.rb +5 -1
- data/lib/gooddata/lcm/lcm2.rb +4 -0
- data/lib/gooddata/models/domain.rb +16 -4
- data/lib/gooddata/models/metadata/label.rb +21 -4
- data/lib/gooddata/models/project.rb +16 -2
- data/lib/gooddata/models/user_filters/user_filter_builder.rb +3 -2
- data/lib/gooddata/rest/connection.rb +13 -7
- metadata +43 -6
@@ -33,6 +33,9 @@ module GoodData
|
|
33
33
|
|
34
34
|
description 'Sync failed list'
|
35
35
|
param :sync_failed_list, instance_of(Type::HashType), required: false
|
36
|
+
|
37
|
+
description 'Number Of Threads'
|
38
|
+
param :number_of_threads, instance_of(Type::StringType), required: false, default: '10'
|
36
39
|
end
|
37
40
|
|
38
41
|
RESULT_HEADER = %i[from to count status]
|
@@ -45,8 +48,9 @@ module GoodData
|
|
45
48
|
|
46
49
|
client = params.gdc_gd_client
|
47
50
|
development_client = params.development_client
|
51
|
+
number_of_threads = Integer(params.number_of_threads || '8')
|
48
52
|
|
49
|
-
params.synchronize.peach do |info|
|
53
|
+
params.synchronize.peach(number_of_threads) do |info|
|
50
54
|
from_project = info.from
|
51
55
|
to_projects = info.to
|
52
56
|
|
@@ -60,7 +64,7 @@ module GoodData
|
|
60
64
|
if dataset_mapping&.dig('datasetMappings', 'items').nil? || dataset_mapping['datasetMappings']['items'].empty?
|
61
65
|
params.gdc_logger.info "Project: '#{from.title}', PID: '#{from.pid}' has no model mapping, skip synchronizing model mapping."
|
62
66
|
else
|
63
|
-
to_projects.peach do |to|
|
67
|
+
to_projects.peach(number_of_threads) do |to|
|
64
68
|
pid = to[:pid]
|
65
69
|
next if sync_failed_project(pid, params)
|
66
70
|
|
@@ -74,6 +74,9 @@ module GoodData
|
|
74
74
|
|
75
75
|
description 'Makes the brick run without altering user filters'
|
76
76
|
param :dry_run, instance_of(Type::StringType), required: false, default: false
|
77
|
+
|
78
|
+
description 'Number Of Threads'
|
79
|
+
param :number_of_threads, instance_of(Type::StringType), required: false, default: '10'
|
77
80
|
end
|
78
81
|
|
79
82
|
class << self
|
@@ -104,6 +107,7 @@ module GoodData
|
|
104
107
|
symbolized_config = GoodData::Helpers.symbolize_keys(symbolized_config)
|
105
108
|
symbolized_config[:labels] = symbolized_config[:labels].map { |l| GoodData::Helpers.symbolize_keys(l) }
|
106
109
|
multiple_projects_column = params.multiple_projects_column
|
110
|
+
number_of_threads = Integer(params.number_of_threads || '10')
|
107
111
|
|
108
112
|
mode = params.sync_mode
|
109
113
|
unless MODES.include?(mode)
|
@@ -196,7 +200,7 @@ module GoodData
|
|
196
200
|
|
197
201
|
unless run_params[:do_not_touch_filters_that_are_not_mentioned]
|
198
202
|
to_be_deleted_clients = UserBricksHelper.non_working_clients(domain_clients, working_client_ids)
|
199
|
-
to_be_deleted_clients.peach do |c|
|
203
|
+
to_be_deleted_clients.peach(number_of_threads) do |c|
|
200
204
|
begin
|
201
205
|
current_project = c.project
|
202
206
|
users = users_by_project[c.client_id]
|
data/lib/gooddata/lcm/lcm2.rb
CHANGED
@@ -295,6 +295,10 @@ module GoodData
|
|
295
295
|
end
|
296
296
|
|
297
297
|
def perform(mode, params = {})
|
298
|
+
# Default setting for $pmap_default_thread_count = 20 in gooddata.rb file. We are going to decrease default
|
299
|
+
# number of threads count to 10 for LCM bricks only
|
300
|
+
$pmap_default_thread_count = 10 # rubocop:disable GlobalVars
|
301
|
+
|
298
302
|
params = convert_params(params)
|
299
303
|
|
300
304
|
GoodData.gd_logger.brick = mode
|
@@ -22,7 +22,15 @@ module GoodData
|
|
22
22
|
'pt-PT' => 'Portuguese/Portugal',
|
23
23
|
'fr-FR' => 'French',
|
24
24
|
'de-DE' => 'German',
|
25
|
-
'ja-JP' => 'Japanese'
|
25
|
+
'ja-JP' => 'Japanese',
|
26
|
+
'it-IT' => 'Italian',
|
27
|
+
'es-419' => 'Spanish/Latin America',
|
28
|
+
'fr-CA' => 'French/Canada',
|
29
|
+
'en-GB' => 'English/UK',
|
30
|
+
'en-AU' => 'English/Australian',
|
31
|
+
'fi-FI' => 'Finnish',
|
32
|
+
'zh-Hant' => 'Traditional Chinese',
|
33
|
+
'zh-HK' => 'Cantonese'
|
26
34
|
}
|
27
35
|
|
28
36
|
class << self
|
@@ -276,12 +284,16 @@ to new properties (email=#{user_data[:email]}, sso_provider=#{user_data[:sso_pro
|
|
276
284
|
[{ type: :successful, :action => :user_changed_in_domain, user: updated_user }]
|
277
285
|
end
|
278
286
|
rescue RuntimeError => e
|
287
|
+
error_message = e.message
|
288
|
+
user.delete(:password)
|
279
289
|
if !domain_user
|
280
|
-
GoodData.logger.error("Failed to add user=#{user_login} to domain=#{default_domain_name}. Error: #{
|
290
|
+
GoodData.logger.error("Failed to add user=#{user_login} to domain=#{default_domain_name}. Error: #{error_message}")
|
281
291
|
else
|
282
|
-
|
292
|
+
error_message = 'Invalid user data or update new password cannot be the same as old password' if error_message == '400 Bad Request'
|
293
|
+
|
294
|
+
GoodData.logger.error("Failed to update user=#{user_login} in domain=#{default_domain_name}. Error: #{error_message}")
|
283
295
|
end
|
284
|
-
[{ type: :failed, :user => user, message:
|
296
|
+
[{ type: :failed, :user => user, message: error_message }]
|
285
297
|
end
|
286
298
|
end
|
287
299
|
end
|
@@ -44,14 +44,31 @@ module GoodData
|
|
44
44
|
# In the case filter a specific value, because the API /validElements only filter by partial match, we need to filter again at client side for exact match.
|
45
45
|
# @return [Array] Results
|
46
46
|
def get_valid_elements(*args)
|
47
|
+
results = {}
|
47
48
|
if args && !args.empty? && args.first[:filter]
|
49
|
+
# Support paging in case filter by a specific value
|
48
50
|
params = args.first
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
51
|
+
all_valid_elements = []
|
52
|
+
offset = 0
|
53
|
+
paging_limit = 10_000
|
54
|
+
|
55
|
+
loop do
|
56
|
+
params[:offset] = offset
|
57
|
+
params[:limit] = paging_limit
|
58
|
+
results, = valid_elements params
|
59
|
+
all_valid_elements += results['validElements']['items'].select do |i|
|
60
|
+
i['element']['title'] == params[:filter]
|
61
|
+
end
|
62
|
+
|
63
|
+
if results['validElements']['items'].count < paging_limit
|
64
|
+
results['validElements']['items'] = all_valid_elements
|
65
|
+
break
|
66
|
+
else
|
67
|
+
offset += paging_limit
|
68
|
+
end
|
53
69
|
end
|
54
70
|
else
|
71
|
+
# This case will support paging by the method which call this method eg: values(...) method
|
55
72
|
results, = valid_elements(*args)
|
56
73
|
end
|
57
74
|
results
|
@@ -1434,9 +1434,13 @@ module GoodData
|
|
1434
1434
|
:crossDataCenterExport => '1'
|
1435
1435
|
}
|
1436
1436
|
}
|
1437
|
-
|
1437
|
+
export_uri = "/gdc/md/#{pid}/maintenance/partialmdexport"
|
1438
|
+
GoodData.gd_logger.info("Project export action=objects_export, project_id=#{pid}, uri=#{export_uri}, export_status=start, export_objs=#{export_payload}") if GoodData.gd_logger
|
1439
|
+
|
1440
|
+
result = client.post(export_uri, export_payload)
|
1438
1441
|
polling_url = result['partialMDArtifact']['status']['uri']
|
1439
1442
|
token = result['partialMDArtifact']['token']
|
1443
|
+
GoodData.gd_logger.info("Project export action=objects_export, project_id=#{pid}, uri=#{polling_url}, export_status=polling") if GoodData.gd_logger
|
1440
1444
|
|
1441
1445
|
polling_result = client.poll_on_response(polling_url, options) do |body|
|
1442
1446
|
body['wTaskStatus'] && body['wTaskStatus']['status'] == 'RUNNING'
|
@@ -1445,6 +1449,9 @@ module GoodData
|
|
1445
1449
|
messages = GoodData::Helpers.interpolate_error_messages(polling_result['wTaskStatus']['messages']).join(' ')
|
1446
1450
|
fail ObjectsExportError, "Exporting objects failed with messages. #{messages}"
|
1447
1451
|
end
|
1452
|
+
|
1453
|
+
GoodData.gd_logger.info("Project export action=objects_export, project_id=#{pid}, export_status=success") if GoodData.gd_logger
|
1454
|
+
|
1448
1455
|
token
|
1449
1456
|
end
|
1450
1457
|
|
@@ -1467,8 +1474,12 @@ module GoodData
|
|
1467
1474
|
}
|
1468
1475
|
}
|
1469
1476
|
|
1470
|
-
|
1477
|
+
import_uri = "/gdc/md/#{pid}/maintenance/partialmdimport"
|
1478
|
+
GoodData.gd_logger.info("Project import action=objects_import, project_id=#{pid}, uri=#{import_uri}, import_status=start") if GoodData.gd_logger
|
1479
|
+
|
1480
|
+
result = client.post(import_uri, import_payload)
|
1471
1481
|
polling_url = result['uri']
|
1482
|
+
GoodData.gd_logger.info("Project import action=objects_import, project_id=#{pid}, uri=#{polling_url}, import_status=polling") if GoodData.gd_logger
|
1472
1483
|
|
1473
1484
|
polling_result = client.poll_on_response(polling_url, options) do |body|
|
1474
1485
|
body['wTaskStatus'] && body['wTaskStatus']['status'] == 'RUNNING'
|
@@ -1478,6 +1489,9 @@ module GoodData
|
|
1478
1489
|
messages = GoodData::Helpers.interpolate_error_messages(polling_result['wTaskStatus']['messages']).join(' ')
|
1479
1490
|
fail ObjectsImportError, "Importing objects failed with messages. #{messages}"
|
1480
1491
|
end
|
1492
|
+
|
1493
|
+
GoodData.gd_logger.info("Project import action=objects_import, project_id=#{pid}, uri=#{import_uri}, import_status=success") if GoodData.gd_logger
|
1494
|
+
|
1481
1495
|
true
|
1482
1496
|
end
|
1483
1497
|
|
@@ -180,7 +180,8 @@ module GoodData
|
|
180
180
|
|
181
181
|
def self.create_lookups_cache(small_labels)
|
182
182
|
small_labels.reduce({}) do |a, e|
|
183
|
-
|
183
|
+
# The validElements API allow maximum paging with 10000 items
|
184
|
+
lookup = e.values(:limit => 10_000).reduce({}) do |a1, e1|
|
184
185
|
a1[e1[:value]] = e1[:uri]
|
185
186
|
a1
|
186
187
|
end
|
@@ -513,7 +514,7 @@ module GoodData
|
|
513
514
|
if to_create.empty?
|
514
515
|
create_results = []
|
515
516
|
else
|
516
|
-
create_results = to_create.each_slice(
|
517
|
+
create_results = to_create.each_slice(50).flat_map do |batch|
|
517
518
|
batch.pmapcat do |related_uri, group|
|
518
519
|
group.each(&:save)
|
519
520
|
res = client.get("/gdc/md/#{project.pid}/userfilters?users=#{related_uri}")
|
@@ -65,7 +65,7 @@ module GoodData
|
|
65
65
|
]
|
66
66
|
|
67
67
|
RETRY_TIME_INITIAL_VALUE = 1
|
68
|
-
RETRY_TIME_COEFFICIENT =
|
68
|
+
RETRY_TIME_COEFFICIENT = 2
|
69
69
|
RETRYABLE_ERRORS << Net::ReadTimeout if Net.const_defined?(:ReadTimeout)
|
70
70
|
|
71
71
|
RETRYABLE_ERRORS << OpenSSL::SSL::SSLErrorWaitReadable if OpenSSL::SSL.const_defined?(:SSLErrorWaitReadable)
|
@@ -94,7 +94,7 @@ module GoodData
|
|
94
94
|
|
95
95
|
# Retry block if exception thrown
|
96
96
|
def retryable(options = {}, &_block)
|
97
|
-
opts = { :tries =>
|
97
|
+
opts = { :tries => 14, :on => RETRYABLE_ERRORS }.merge(options)
|
98
98
|
|
99
99
|
retry_exception = opts[:on]
|
100
100
|
retries = opts[:tries]
|
@@ -114,21 +114,27 @@ module GoodData
|
|
114
114
|
if (retries -= 1) > 0
|
115
115
|
retry
|
116
116
|
else
|
117
|
-
|
117
|
+
process_retry_error(e, retry_time)
|
118
118
|
end
|
119
119
|
rescue RestClient::TooManyRequests, RestClient::ServiceUnavailable, *retry_exception => e
|
120
|
-
GoodData.logger.warn "#{e.message}, retrying in #{retry_time} seconds"
|
121
120
|
sleep retry_time
|
122
|
-
|
123
|
-
# 10 requests with 1.5 coefficent should take ~ 3 mins to finish
|
121
|
+
# Total 10 retry requests with 1.5 coefficent should take ~ 2 mins to finish
|
124
122
|
if (retries -= 1) > 0
|
123
|
+
retry_time *= RETRY_TIME_COEFFICIENT
|
125
124
|
retry
|
126
125
|
else
|
127
|
-
|
126
|
+
process_retry_error(e, retry_time)
|
128
127
|
end
|
129
128
|
end
|
130
129
|
yield
|
131
130
|
end
|
131
|
+
|
132
|
+
def process_retry_error(e, retry_time)
|
133
|
+
error_message = "#{e.message}, retrying in #{retry_time} seconds"
|
134
|
+
GoodData.logger.warn error_message
|
135
|
+
GoodData.gd_logger.warn error_message
|
136
|
+
fail e
|
137
|
+
end
|
132
138
|
end
|
133
139
|
|
134
140
|
attr_reader :request_params
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gooddata
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.3.
|
4
|
+
version: 2.3.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Pavel Kolesnikov
|
@@ -14,7 +14,7 @@ authors:
|
|
14
14
|
autorequire:
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
|
-
date:
|
17
|
+
date: 2025-04-29 00:00:00.000000000 Z
|
18
18
|
dependencies:
|
19
19
|
- !ruby/object:Gem::Dependency
|
20
20
|
name: license_finder
|
@@ -114,6 +114,26 @@ dependencies:
|
|
114
114
|
- - ">="
|
115
115
|
- !ruby/object:Gem::Version
|
116
116
|
version: '1.28'
|
117
|
+
- !ruby/object:Gem::Dependency
|
118
|
+
name: rubocop-ast
|
119
|
+
requirement: !ruby/object:Gem::Requirement
|
120
|
+
requirements:
|
121
|
+
- - ">="
|
122
|
+
- !ruby/object:Gem::Version
|
123
|
+
version: 1.24.1
|
124
|
+
- - "<="
|
125
|
+
- !ruby/object:Gem::Version
|
126
|
+
version: 1.42.0
|
127
|
+
type: :development
|
128
|
+
prerelease: false
|
129
|
+
version_requirements: !ruby/object:Gem::Requirement
|
130
|
+
requirements:
|
131
|
+
- - ">="
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: 1.24.1
|
134
|
+
- - "<="
|
135
|
+
- !ruby/object:Gem::Version
|
136
|
+
version: 1.42.0
|
117
137
|
- !ruby/object:Gem::Dependency
|
118
138
|
name: simplecov
|
119
139
|
requirement: !ruby/object:Gem::Requirement
|
@@ -286,16 +306,16 @@ dependencies:
|
|
286
306
|
name: activesupport
|
287
307
|
requirement: !ruby/object:Gem::Requirement
|
288
308
|
requirements:
|
289
|
-
- - "
|
309
|
+
- - ">="
|
290
310
|
- !ruby/object:Gem::Version
|
291
|
-
version:
|
311
|
+
version: 6.0.3.1
|
292
312
|
type: :runtime
|
293
313
|
prerelease: false
|
294
314
|
version_requirements: !ruby/object:Gem::Requirement
|
295
315
|
requirements:
|
296
|
-
- - "
|
316
|
+
- - ">="
|
297
317
|
- !ruby/object:Gem::Version
|
298
|
-
version:
|
318
|
+
version: 6.0.3.1
|
299
319
|
- !ruby/object:Gem::Dependency
|
300
320
|
name: net-smtp
|
301
321
|
requirement: !ruby/object:Gem::Requirement
|
@@ -470,6 +490,20 @@ dependencies:
|
|
470
490
|
- - "~>"
|
471
491
|
- !ruby/object:Gem::Version
|
472
492
|
version: '1.0'
|
493
|
+
- !ruby/object:Gem::Dependency
|
494
|
+
name: path_expander
|
495
|
+
requirement: !ruby/object:Gem::Requirement
|
496
|
+
requirements:
|
497
|
+
- - "<"
|
498
|
+
- !ruby/object:Gem::Version
|
499
|
+
version: 1.1.2
|
500
|
+
type: :runtime
|
501
|
+
prerelease: false
|
502
|
+
version_requirements: !ruby/object:Gem::Requirement
|
503
|
+
requirements:
|
504
|
+
- - "<"
|
505
|
+
- !ruby/object:Gem::Version
|
506
|
+
version: 1.1.2
|
473
507
|
- !ruby/object:Gem::Dependency
|
474
508
|
name: pmap
|
475
509
|
requirement: !ruby/object:Gem::Requirement
|
@@ -637,6 +671,9 @@ files:
|
|
637
671
|
- ".gdc-ii-config-chart.yaml"
|
638
672
|
- ".gdc-ii-config.yaml"
|
639
673
|
- ".github/workflows/build.yml"
|
674
|
+
- ".github/workflows/check-extended.yaml"
|
675
|
+
- ".github/workflows/check.yaml"
|
676
|
+
- ".github/workflows/gate.yaml"
|
640
677
|
- ".github/workflows/pre-merge.yml"
|
641
678
|
- ".gitignore"
|
642
679
|
- ".pronto.yml"
|