gooddata 2.2.0 → 2.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.gdc-ii-config.yaml +42 -1
- data/.github/workflows/build.yml +14 -13
- data/.github/workflows/pre-merge.yml +13 -13
- data/.pronto.yml +1 -0
- data/.rubocop.yml +2 -14
- data/CHANGELOG.md +9 -0
- data/Dockerfile +13 -7
- data/Dockerfile.jruby +5 -5
- data/Dockerfile.ruby +5 -7
- data/Gemfile +4 -2
- data/README.md +5 -4
- data/Rakefile +1 -1
- data/SDK_VERSION +1 -1
- data/VERSION +1 -1
- data/bin/run_brick.rb +7 -0
- data/ci/mysql/pom.xml +6 -1
- data/ci/redshift/pom.xml +3 -4
- data/docker-compose.lcm.yml +42 -1
- data/docker-compose.yml +42 -0
- data/gooddata.gemspec +21 -22
- data/lcm.rake +9 -0
- data/lib/gooddata/bricks/base_pipeline.rb +26 -0
- data/lib/gooddata/bricks/brick.rb +0 -1
- data/lib/gooddata/bricks/middleware/execution_result_middleware.rb +3 -3
- data/lib/gooddata/bricks/pipeline.rb +2 -14
- data/lib/gooddata/cloud_resources/mysql/mysql_client.rb +18 -8
- data/lib/gooddata/cloud_resources/redshift/drivers/.gitkeepme +0 -0
- data/lib/gooddata/cloud_resources/redshift/redshift_client.rb +0 -2
- data/lib/gooddata/cloud_resources/snowflake/snowflake_client.rb +1 -1
- data/lib/gooddata/lcm/actions/base_action.rb +157 -0
- data/lib/gooddata/lcm/actions/collect_data_product.rb +2 -1
- data/lib/gooddata/lcm/actions/collect_projects_warning_status.rb +53 -0
- data/lib/gooddata/lcm/actions/collect_segment_clients.rb +14 -0
- data/lib/gooddata/lcm/actions/initialize_continue_on_error_option.rb +87 -0
- data/lib/gooddata/lcm/actions/migrate_gdc_date_dimension.rb +28 -2
- data/lib/gooddata/lcm/actions/provision_clients.rb +34 -5
- data/lib/gooddata/lcm/actions/synchronize_cas.rb +24 -4
- data/lib/gooddata/lcm/actions/synchronize_clients.rb +56 -4
- data/lib/gooddata/lcm/actions/synchronize_dataset_mappings.rb +28 -3
- data/lib/gooddata/lcm/actions/synchronize_etls_in_segment.rb +48 -11
- data/lib/gooddata/lcm/actions/synchronize_kd_dashboard_permission.rb +103 -0
- data/lib/gooddata/lcm/actions/synchronize_ldm.rb +60 -15
- data/lib/gooddata/lcm/actions/synchronize_ldm_layout.rb +98 -0
- data/lib/gooddata/lcm/actions/synchronize_pp_dashboard_permission.rb +108 -0
- data/lib/gooddata/lcm/actions/synchronize_schedules.rb +31 -1
- data/lib/gooddata/lcm/actions/synchronize_user_filters.rb +14 -9
- data/lib/gooddata/lcm/actions/synchronize_user_groups.rb +30 -4
- data/lib/gooddata/lcm/actions/synchronize_users.rb +11 -10
- data/lib/gooddata/lcm/actions/update_metric_formats.rb +21 -4
- data/lib/gooddata/lcm/exceptions/lcm_execution_warning.rb +15 -0
- data/lib/gooddata/lcm/helpers/check_helper.rb +19 -0
- data/lib/gooddata/lcm/lcm2.rb +45 -4
- data/lib/gooddata/lcm/user_bricks_helper.rb +9 -0
- data/lib/gooddata/mixins/inspector.rb +1 -1
- data/lib/gooddata/models/ldm_layout.rb +38 -0
- data/lib/gooddata/models/project.rb +197 -22
- data/lib/gooddata/models/project_creator.rb +83 -6
- data/lib/gooddata/models/segment.rb +2 -1
- data/lib/gooddata/models/user_filters/user_filter_builder.rb +104 -15
- data/lib/gooddata/rest/connection.rb +5 -3
- data/lib/gooddata/rest/phmap.rb +1 -0
- data/lib/gooddata.rb +1 -0
- data/lib/gooddata_brick_base.rb +35 -0
- data/sonar-project.properties +6 -0
- metadata +64 -59
- data/lib/gooddata/cloud_resources/redshift/drivers/log4j.properties +0 -15
@@ -19,6 +19,18 @@ module GoodData
|
|
19
19
|
|
20
20
|
description 'Synchronization Info'
|
21
21
|
param :synchronize, array_of(instance_of(Type::SynchronizationInfoType)), required: true, generated: true
|
22
|
+
|
23
|
+
description 'Abort on error'
|
24
|
+
param :abort_on_error, instance_of(Type::StringType), required: false
|
25
|
+
|
26
|
+
description 'Logger'
|
27
|
+
param :gdc_logger, instance_of(Type::GdLogger), required: false
|
28
|
+
|
29
|
+
description 'Collect synced status'
|
30
|
+
param :collect_synced_status, instance_of(Type::BooleanType), required: false
|
31
|
+
|
32
|
+
description 'Sync failed list'
|
33
|
+
param :sync_failed_list, instance_of(Type::HashType), required: false
|
22
34
|
end
|
23
35
|
|
24
36
|
RESULT_HEADER = %i[from to status]
|
@@ -27,6 +39,8 @@ module GoodData
|
|
27
39
|
def call(params)
|
28
40
|
results = []
|
29
41
|
params.synchronize.map do |segment_info|
|
42
|
+
next if sync_failed_segment(segment_info[:segment_id], params)
|
43
|
+
|
30
44
|
result = migrate_date_dimension(params, segment_info)
|
31
45
|
results.concat(result)
|
32
46
|
end
|
@@ -47,8 +61,13 @@ module GoodData
|
|
47
61
|
# check latest master and previous master
|
48
62
|
master_upgrade_datasets = get_upgrade_dates(latest_blueprint, previous_blueprint) if params[:synchronize_ldm].downcase == 'diff_against_master' && previous_blueprint
|
49
63
|
unless master_upgrade_datasets&.empty?
|
64
|
+
collect_synced_status = collect_synced_status(params)
|
65
|
+
failed_projects = ThreadSafe::Array.new
|
66
|
+
|
50
67
|
segment_info[:to].pmap do |entry|
|
51
68
|
pid = entry[:pid]
|
69
|
+
next if sync_failed_project(pid, params)
|
70
|
+
|
52
71
|
to_project = client.projects(pid) || fail("Invalid 'to' project specified - '#{pid}'")
|
53
72
|
GoodData.logger.info "Migrating date dimension, project: '#{to_project.title}', PID: #{pid}"
|
54
73
|
to_blueprint = to_project.blueprint
|
@@ -56,13 +75,18 @@ module GoodData
|
|
56
75
|
next if upgrade_datasets.empty?
|
57
76
|
|
58
77
|
message = get_upgrade_message(upgrade_datasets)
|
78
|
+
failed_message = "Failed to migrate date dimension for project #{pid}"
|
79
|
+
update_status = to_project.upgrade_custom_v2(message)
|
80
|
+
process_failed_project(pid, failed_message, failed_projects, collect_synced_status) if collect_synced_status && update_status != 'OK'
|
59
81
|
|
60
82
|
results << {
|
61
83
|
from: segment_info[:from],
|
62
84
|
to: pid,
|
63
|
-
status:
|
85
|
+
status: update_status
|
64
86
|
}
|
65
87
|
end
|
88
|
+
|
89
|
+
process_failed_projects(failed_projects, short_name, params) if collect_synced_status
|
66
90
|
end
|
67
91
|
|
68
92
|
results
|
@@ -104,8 +128,10 @@ module GoodData
|
|
104
128
|
get_date_dimensions(blueprint).any? { |e| e[:urn] == DATE_DIMENSION_CUSTOM_V2 }
|
105
129
|
end
|
106
130
|
|
131
|
+
# Get date dimension from blue print. Return nil if date dimension not existing
|
107
132
|
def get_date_dimension(blueprint, id)
|
108
|
-
|
133
|
+
date_dimensions = get_date_dimensions(blueprint)
|
134
|
+
date_dimensions.find { |d| d[:id] == id }
|
109
135
|
end
|
110
136
|
|
111
137
|
def get_date_dimensions(blueprint)
|
@@ -29,7 +29,16 @@ module GoodData
|
|
29
29
|
param :data_product, instance_of(Type::GDDataProductType), required: false
|
30
30
|
|
31
31
|
description 'Logger'
|
32
|
-
param :gdc_logger, instance_of(Type::GdLogger), required:
|
32
|
+
param :gdc_logger, instance_of(Type::GdLogger), required: false
|
33
|
+
|
34
|
+
description 'Abort on error'
|
35
|
+
param :abort_on_error, instance_of(Type::StringType), required: false
|
36
|
+
|
37
|
+
description 'Collect synced status'
|
38
|
+
param :collect_synced_status, instance_of(Type::BooleanType), required: false
|
39
|
+
|
40
|
+
description 'Sync failed list'
|
41
|
+
param :sync_failed_list, instance_of(Type::HashType), required: false
|
33
42
|
end
|
34
43
|
|
35
44
|
RESULT_HEADER = [
|
@@ -45,6 +54,8 @@ module GoodData
|
|
45
54
|
synchronize_projects = []
|
46
55
|
data_product = params.data_product
|
47
56
|
client = params.gdc_gd_client
|
57
|
+
collect_synced_status = collect_synced_status(params)
|
58
|
+
continue_on_error = continue_on_error(params)
|
48
59
|
domain_name = params.organization || params.domain
|
49
60
|
fail "Either organisation or domain has to be specified in params" unless domain_name
|
50
61
|
domain = client.domain(domain_name) || fail("Invalid domain name specified - #{domain_name}")
|
@@ -52,6 +63,8 @@ module GoodData
|
|
52
63
|
invalid_client_ids = []
|
53
64
|
begin
|
54
65
|
results = params.segments.map do |segment|
|
66
|
+
next if sync_failed_segment(segment.segment_id, params)
|
67
|
+
|
55
68
|
segment_object = domain.segments(segment.segment_id, data_product)
|
56
69
|
tmp = segment_object.provision_client_projects.map do |m|
|
57
70
|
Hash[m.each_pair.to_a].merge(type: :provision_result)
|
@@ -65,10 +78,21 @@ module GoodData
|
|
65
78
|
unless entry[:project_uri]
|
66
79
|
error_message = "There was error during provisioning clients: #{entry[:error]}" unless error_message
|
67
80
|
invalid_client_ids << entry[:id]
|
81
|
+
if collect_synced_status
|
82
|
+
failed_message = "Failed to provision client #{entry[:id]} in segment #{segment.segment_id}. Error: #{entry[:error]}"
|
83
|
+
add_failed_client(entry[:id], failed_message, short_name, params)
|
84
|
+
end
|
68
85
|
next
|
69
86
|
end
|
87
|
+
|
88
|
+
project_id = entry[:project_uri].split('/').last
|
89
|
+
if collect_synced_status && entry[:status] == 'CREATED' && entry[:id]
|
90
|
+
# Update project client mappings when there are create new clients during provision clients of the segment
|
91
|
+
add_new_clients_to_project_client_mapping(project_id, entry[:id], segment.segment_id, params)
|
92
|
+
end
|
93
|
+
|
70
94
|
{
|
71
|
-
pid:
|
95
|
+
pid: project_id,
|
72
96
|
client_id: entry[:id]
|
73
97
|
}
|
74
98
|
end.compact
|
@@ -93,15 +117,20 @@ module GoodData
|
|
93
117
|
end
|
94
118
|
|
95
119
|
params.gdc_logger.debug "Deleted clients: #{deleted_client_ids.join(', ')}"
|
96
|
-
|
120
|
+
unless error_message['TooManyProjectsCreatedException'] || error_message['Max number registered projects']
|
121
|
+
raise error_message unless continue_on_error
|
122
|
+
|
123
|
+
next
|
124
|
+
end
|
125
|
+
|
97
126
|
break tmp
|
98
127
|
end
|
99
128
|
|
100
129
|
tmp
|
101
130
|
end
|
102
131
|
rescue => e
|
103
|
-
params.gdc_logger.error "Problem occurs when provisioning clients."
|
104
|
-
raise e
|
132
|
+
params.gdc_logger.error "Problem occurs when provisioning clients. Error: #{e}"
|
133
|
+
raise e unless continue_on_error
|
105
134
|
end
|
106
135
|
|
107
136
|
results.flatten! if results
|
@@ -32,6 +32,15 @@ module GoodData
|
|
32
32
|
|
33
33
|
description 'Additional Hidden Parameters'
|
34
34
|
param :additional_hidden_params, instance_of(Type::HashType), required: false
|
35
|
+
|
36
|
+
description 'Abort on error'
|
37
|
+
param :abort_on_error, instance_of(Type::StringType), required: false
|
38
|
+
|
39
|
+
description 'Collect synced status'
|
40
|
+
param :collect_synced_status, instance_of(Type::BooleanType), required: false
|
41
|
+
|
42
|
+
description 'Sync failed list'
|
43
|
+
param :sync_failed_list, instance_of(Type::HashType), required: false
|
35
44
|
end
|
36
45
|
|
37
46
|
class << self
|
@@ -46,6 +55,8 @@ module GoodData
|
|
46
55
|
return results unless include_ca
|
47
56
|
|
48
57
|
client = params.gdc_gd_client
|
58
|
+
collect_synced_status = collect_synced_status(params)
|
59
|
+
failed_projects = ThreadSafe::Array.new
|
49
60
|
|
50
61
|
params.synchronize.each do |info|
|
51
62
|
from = info.from
|
@@ -58,24 +69,33 @@ module GoodData
|
|
58
69
|
next unless ca_scripts
|
59
70
|
|
60
71
|
pid = entry[:pid]
|
72
|
+
next if sync_failed_project(pid, params)
|
73
|
+
|
61
74
|
ca_chunks = ca_scripts[:maqlDdlChunks]
|
62
|
-
to_project = client.projects(pid)
|
63
|
-
|
75
|
+
to_project = client.projects(pid)
|
76
|
+
unless to_project
|
77
|
+
process_failed_project(pid, "Invalid 'to' project specified - '#{pid}'", failed_projects, collect_synced_status)
|
78
|
+
next
|
79
|
+
end
|
64
80
|
|
81
|
+
params.gdc_logger.info "Synchronizing Computed Attributes to project: '#{to_project.title}', PID: #{pid}"
|
82
|
+
error_message = nil
|
65
83
|
begin
|
66
84
|
ca_chunks.each { |chunk| to_project.execute_maql(chunk) }
|
67
85
|
rescue => e
|
68
|
-
|
86
|
+
error_message = "Error occurred when executing MAQL for project: \"#{to_project.title}\" reason: \"#{e.message}\", chunks: #{ca_chunks.inspect}"
|
87
|
+
process_failed_project(pid, error_message, failed_projects, collect_synced_status)
|
69
88
|
end
|
70
89
|
|
71
90
|
results << {
|
72
91
|
from: from,
|
73
92
|
to: pid,
|
74
|
-
status: 'ok'
|
93
|
+
status: error_message.nil? ? 'ok' : 'failed'
|
75
94
|
}
|
76
95
|
end
|
77
96
|
end
|
78
97
|
|
98
|
+
process_failed_projects(failed_projects, short_name, params) if collect_synced_status
|
79
99
|
results
|
80
100
|
end
|
81
101
|
end
|
@@ -38,6 +38,18 @@ module GoodData
|
|
38
38
|
|
39
39
|
description 'Additional Hidden Parameters'
|
40
40
|
param :additional_hidden_params, instance_of(Type::HashType), required: false
|
41
|
+
|
42
|
+
description 'Abort on error'
|
43
|
+
param :abort_on_error, instance_of(Type::StringType), required: false
|
44
|
+
|
45
|
+
description 'Logger'
|
46
|
+
param :gdc_logger, instance_of(Type::GdLogger), required: false
|
47
|
+
|
48
|
+
description 'Collect synced status'
|
49
|
+
param :collect_synced_status, instance_of(Type::BooleanType), required: false
|
50
|
+
|
51
|
+
description 'Sync failed list'
|
52
|
+
param :sync_failed_list, instance_of(Type::HashType), required: false
|
41
53
|
end
|
42
54
|
|
43
55
|
RESULT_HEADER = [
|
@@ -65,6 +77,8 @@ module GoodData
|
|
65
77
|
end
|
66
78
|
|
67
79
|
results = segments.map do |segment|
|
80
|
+
next if sync_failed_segment(segment.segment_id, params)
|
81
|
+
|
68
82
|
if params.ads_client
|
69
83
|
master_projects = GoodData::LCM2::Helpers.get_master_project_list_from_ads(params.release_table_name, params.ads_client, segment.segment_id)
|
70
84
|
else
|
@@ -83,8 +97,7 @@ module GoodData
|
|
83
97
|
failed_count = sync_result['failedClients']['count']
|
84
98
|
|
85
99
|
if failed_count.to_i > 0
|
86
|
-
|
87
|
-
"Details: #{sync_result['links']['details']}")
|
100
|
+
error_handle(segment, res, collect_synced_status(params), params)
|
88
101
|
end
|
89
102
|
|
90
103
|
if keep_only_previous_masters_count >= 0
|
@@ -102,8 +115,8 @@ module GoodData
|
|
102
115
|
|
103
116
|
{
|
104
117
|
segment: segment.id,
|
105
|
-
master_pid: master.pid,
|
106
|
-
master_name: master.title,
|
118
|
+
master_pid: master.nil? ? '' : master.pid,
|
119
|
+
master_name: master.nil? ? '' : master.title,
|
107
120
|
successful_count: sync_result['successfulClients']['count']
|
108
121
|
}
|
109
122
|
end
|
@@ -147,6 +160,45 @@ module GoodData
|
|
147
160
|
end
|
148
161
|
end
|
149
162
|
# rubocop:enable Metrics/ParameterLists
|
163
|
+
|
164
|
+
private
|
165
|
+
|
166
|
+
def error_handle(segment, error_result, continue_on_error, params)
|
167
|
+
sync_result = error_result.json['synchronizationResult']
|
168
|
+
success_count = sync_result['successfulClients']['count'].to_i
|
169
|
+
failed_count = sync_result['failedClients']['count'].to_i
|
170
|
+
|
171
|
+
# Synchronize failure for all clients in segment
|
172
|
+
if continue_on_error && success_count.zero? && failed_count.positive?
|
173
|
+
segment_warning_message = "Failed to synchronize clients for #{segment.segment_id} segment. Details: #{sync_result['links']['details']}"
|
174
|
+
add_failed_segment(segment.segment_id, segment_warning_message, short_name, params)
|
175
|
+
return
|
176
|
+
end
|
177
|
+
|
178
|
+
summary_error_message = "#{failed_count} clients failed to synchronize. Details: #{sync_result['links']['details']}"
|
179
|
+
fail(summary_error_message) unless continue_on_error
|
180
|
+
|
181
|
+
GoodData.logger.warn summary_error_message
|
182
|
+
if error_result.details # rubocop:disable Style/SafeNavigation
|
183
|
+
error_result.details.items.each do |item|
|
184
|
+
next if item['status'] == 'OK'
|
185
|
+
|
186
|
+
error_client_id = item['id']
|
187
|
+
error_message = error_message(item, segment)
|
188
|
+
add_failed_client(error_client_id, error_message, short_name, params)
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
def error_message(error_item, segment)
|
194
|
+
error_client_id = error_item['id']
|
195
|
+
error_message = "Failed to synchronize #{error_client_id} client in #{segment.segment_id} segment."
|
196
|
+
error_message = "#{error_message}. Detail: #{error_item['error']['message']}" if error_item['error'] && error_item['error']['message']
|
197
|
+
|
198
|
+
error_message = "#{error_message}. Error items: #{error_item['error']['parameters']}" if error_item['error'] && error_item['error']['parameters']
|
199
|
+
|
200
|
+
error_message
|
201
|
+
end
|
150
202
|
end
|
151
203
|
end
|
152
204
|
end
|
@@ -24,6 +24,15 @@ module GoodData
|
|
24
24
|
|
25
25
|
description 'Logger'
|
26
26
|
param :gdc_logger, instance_of(Type::GdLogger), required: true
|
27
|
+
|
28
|
+
description 'Abort on error'
|
29
|
+
param :abort_on_error, instance_of(Type::StringType), required: false
|
30
|
+
|
31
|
+
description 'Collect synced status'
|
32
|
+
param :collect_synced_status, instance_of(Type::BooleanType), required: false
|
33
|
+
|
34
|
+
description 'Sync failed list'
|
35
|
+
param :sync_failed_list, instance_of(Type::HashType), required: false
|
27
36
|
end
|
28
37
|
|
29
38
|
RESULT_HEADER = %i[from to count status]
|
@@ -31,6 +40,8 @@ module GoodData
|
|
31
40
|
class << self
|
32
41
|
def call(params)
|
33
42
|
results = []
|
43
|
+
collect_synced_status = collect_synced_status(params)
|
44
|
+
failed_projects = ThreadSafe::Array.new
|
34
45
|
|
35
46
|
client = params.gdc_gd_client
|
36
47
|
development_client = params.development_client
|
@@ -39,22 +50,36 @@ module GoodData
|
|
39
50
|
from_project = info.from
|
40
51
|
to_projects = info.to
|
41
52
|
|
42
|
-
from = development_client.projects(from_project)
|
53
|
+
from = development_client.projects(from_project)
|
54
|
+
unless from
|
55
|
+
process_failed_project(from_project, "Invalid 'from' project specified - '#{from_project}'", failed_projects, collect_synced_status)
|
56
|
+
next
|
57
|
+
end
|
58
|
+
|
43
59
|
dataset_mapping = from.dataset_mapping
|
44
60
|
if dataset_mapping&.dig('datasetMappings', 'items').nil? || dataset_mapping['datasetMappings']['items'].empty?
|
45
61
|
params.gdc_logger.info "Project: '#{from.title}', PID: '#{from.pid}' has no model mapping, skip synchronizing model mapping."
|
46
62
|
else
|
47
63
|
to_projects.peach do |to|
|
48
64
|
pid = to[:pid]
|
49
|
-
|
65
|
+
next if sync_failed_project(pid, params)
|
66
|
+
|
67
|
+
to_project = client.projects(pid)
|
68
|
+
process_failed_project(pid, "Invalid 'to' project specified - '#{pid}'", failed_projects, collect_synced_status) unless to_project
|
50
69
|
|
51
|
-
|
70
|
+
message_to_project = "to project: '#{to_project.title}', PID: '#{to_project.pid}'"
|
71
|
+
params.gdc_logger.info "Transferring model mapping, from project: '#{from.title}', PID: '#{from.pid}', #{message_to_project}"
|
52
72
|
res = to_project.update_dataset_mapping(dataset_mapping)
|
53
73
|
res[:from] = from.pid
|
54
74
|
results << res
|
75
|
+
|
76
|
+
failed_message = "Failed to transfer model mapping from project '#{from.pid}' to project '#{to_project.pid}'"
|
77
|
+
process_failed_project(pid, failed_message, failed_projects, collect_synced_status) if collect_synced_status && res[:status] != 'OK'
|
55
78
|
end
|
56
79
|
end
|
57
80
|
end
|
81
|
+
|
82
|
+
process_failed_projects(failed_projects, short_name, params) if collect_synced_status
|
58
83
|
# Return results
|
59
84
|
results.flatten
|
60
85
|
end
|
@@ -44,6 +44,15 @@ module GoodData
|
|
44
44
|
|
45
45
|
description 'Delete extra process schedule flag'
|
46
46
|
param :delete_extra_process_schedule, instance_of(Type::BooleanType), required: false, default: true
|
47
|
+
|
48
|
+
description 'Abort on error'
|
49
|
+
param :abort_on_error, instance_of(Type::StringType), required: false
|
50
|
+
|
51
|
+
description 'Collect synced status'
|
52
|
+
param :collect_synced_status, instance_of(Type::BooleanType), required: false
|
53
|
+
|
54
|
+
description 'Sync failed list'
|
55
|
+
param :sync_failed_list, instance_of(Type::HashType), required: false
|
47
56
|
end
|
48
57
|
|
49
58
|
# will be updated later based on the way etl synchronization
|
@@ -59,6 +68,8 @@ module GoodData
|
|
59
68
|
def call(params)
|
60
69
|
client = params.gdc_gd_client
|
61
70
|
data_product = params.data_product
|
71
|
+
collect_synced_status = collect_synced_status(params)
|
72
|
+
failed_projects = ThreadSafe::Array.new
|
62
73
|
|
63
74
|
schedule_additional_params = params.schedule_additional_params || params.additional_params
|
64
75
|
schedule_additional_hidden_params = params.schedule_additional_hidden_params || params.additional_hidden_params
|
@@ -68,6 +79,8 @@ module GoodData
|
|
68
79
|
end
|
69
80
|
|
70
81
|
results = synchronize_segments.pmap do |segment_id, synchronize|
|
82
|
+
next if collect_synced_status && sync_failed_segment(segment_id, params)
|
83
|
+
|
71
84
|
segment = data_product.segments.find { |s| s.segment_id == segment_id }
|
72
85
|
res = segment.synchronize_processes(
|
73
86
|
synchronize.flat_map do |info|
|
@@ -80,7 +93,11 @@ module GoodData
|
|
80
93
|
res = GoodData::Helpers.symbolize_keys(res)
|
81
94
|
|
82
95
|
if res[:syncedResult][:errors]
|
83
|
-
|
96
|
+
error_message = "Failed to sync processes/schedules for segment #{segment_id}. Error: #{res[:syncedResult][:errors].pretty_inspect}"
|
97
|
+
fail error_message unless collect_synced_status
|
98
|
+
|
99
|
+
add_failed_segment(segment_id, error_message, short_name, params)
|
100
|
+
next
|
84
101
|
end
|
85
102
|
|
86
103
|
if res[:syncedResult][:clients]
|
@@ -111,12 +128,19 @@ module GoodData
|
|
111
128
|
hidden_params_for_all_schedules_in_all_projects = hidden_params_for_all_projects[:all_schedules]
|
112
129
|
|
113
130
|
params.synchronize.peach do |info|
|
114
|
-
from_project_etl_names = get_process_n_schedule_names(client, info.from) if delete_extra_process_schedule
|
131
|
+
from_project_etl_names = get_process_n_schedule_names(client, info.from, failed_projects, collect_synced_status) if delete_extra_process_schedule
|
132
|
+
next if delete_extra_process_schedule && from_project_etl_names.nil?
|
115
133
|
|
116
134
|
to_projects = info.to
|
117
135
|
to_projects.peach do |entry|
|
118
136
|
pid = entry[:pid]
|
119
|
-
|
137
|
+
next if collect_synced_status && sync_failed_project(pid, params)
|
138
|
+
|
139
|
+
to_project = client.projects(pid)
|
140
|
+
unless to_project
|
141
|
+
process_failed_project(pid, "Invalid 'to' project specified - '#{pid}'", failed_projects, collect_synced_status)
|
142
|
+
next
|
143
|
+
end
|
120
144
|
|
121
145
|
if delete_extra_process_schedule
|
122
146
|
to_project_process_id_names = {}
|
@@ -140,24 +164,31 @@ module GoodData
|
|
140
164
|
to_project.set_metadata('GOODOT_CUSTOM_PROJECT_ID', client_id) # TMA-210
|
141
165
|
|
142
166
|
to_project.schedules.each do |schedule|
|
167
|
+
schedule_name = schedule.name
|
143
168
|
if delete_extra_process_schedule
|
144
|
-
|
169
|
+
schedule_project_info = [schedule_name, to_project_process_id_names[schedule.process_id]]
|
170
|
+
unless from_project_etl_names[:schedules].include?(schedule_project_info)
|
145
171
|
schedule.delete
|
146
172
|
next
|
147
173
|
end
|
148
174
|
end
|
149
175
|
|
176
|
+
params_for_all_projects_schedule_name = params_for_all_projects[schedule_name]
|
177
|
+
params_for_this_client_schedule_name = params_for_this_client[schedule_name]
|
178
|
+
hidden_params_for_all_projects_schedule_name = hidden_params_for_all_projects[schedule_name]
|
179
|
+
hidden_params_for_this_client_schedule_name = hidden_params_for_this_client[schedule_name]
|
180
|
+
|
150
181
|
schedule.update_params(schedule_additional_params) if schedule_additional_params
|
151
|
-
schedule.update_params(params_for_all_schedules_in_all_projects) if params_for_all_schedules_in_all_projects
|
152
|
-
schedule.update_params(
|
182
|
+
schedule.update_params(**params_for_all_schedules_in_all_projects) if params_for_all_schedules_in_all_projects
|
183
|
+
schedule.update_params(**params_for_all_projects_schedule_name) if params_for_all_projects_schedule_name
|
153
184
|
schedule.update_params(params_for_all_schedules_in_this_client) if params_for_all_schedules_in_this_client
|
154
|
-
schedule.update_params(
|
185
|
+
schedule.update_params(**params_for_this_client_schedule_name) if params_for_this_client_schedule_name
|
155
186
|
|
156
187
|
schedule.update_hidden_params(schedule_additional_hidden_params) if schedule_additional_hidden_params
|
157
188
|
schedule.update_hidden_params(hidden_params_for_all_schedules_in_all_projects) if hidden_params_for_all_schedules_in_all_projects
|
158
|
-
schedule.update_hidden_params(
|
189
|
+
schedule.update_hidden_params(hidden_params_for_all_projects_schedule_name) if hidden_params_for_all_projects_schedule_name
|
159
190
|
schedule.update_hidden_params(hidden_params_for_all_schedules_in_this_client) if hidden_params_for_all_schedules_in_this_client
|
160
|
-
schedule.update_hidden_params(
|
191
|
+
schedule.update_hidden_params(hidden_params_for_this_client_schedule_name) if hidden_params_for_this_client_schedule_name
|
161
192
|
|
162
193
|
schedule.enable
|
163
194
|
schedule.save
|
@@ -165,13 +196,19 @@ module GoodData
|
|
165
196
|
end
|
166
197
|
end
|
167
198
|
|
199
|
+
process_failed_projects(failed_projects, short_name, params) if collect_synced_status
|
168
200
|
results.flatten
|
169
201
|
end
|
170
202
|
|
171
203
|
private
|
172
204
|
|
173
|
-
def get_process_n_schedule_names(client, project_id)
|
174
|
-
project = client.projects(project_id)
|
205
|
+
def get_process_n_schedule_names(client, project_id, failed_projects, continue_on_error)
|
206
|
+
project = client.projects(project_id)
|
207
|
+
unless project
|
208
|
+
process_failed_project(project_id, "Invalid 'from' project specified - '#{project_id}'", failed_projects, continue_on_error)
|
209
|
+
return nil
|
210
|
+
end
|
211
|
+
|
175
212
|
processes = project.processes
|
176
213
|
process_id_names = Hash[processes.map { |process| [process.process_id, process.name] }]
|
177
214
|
|
@@ -0,0 +1,103 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
#
|
4
|
+
# Copyright (c) 2010-2021 GoodData Corporation. All rights reserved.
|
5
|
+
# This source code is licensed under the BSD-style license found in the
|
6
|
+
# LICENSE file in the root directory of this source tree.
|
7
|
+
|
8
|
+
require_relative 'base_action'
|
9
|
+
require 'thread_safe'
|
10
|
+
|
11
|
+
module GoodData
|
12
|
+
module LCM2
|
13
|
+
class SynchronizeKDDashboardPermissions < BaseAction
|
14
|
+
DESCRIPTION = 'Synchronize KD Dashboard Permission'
|
15
|
+
|
16
|
+
PARAMS = define_params(self) do
|
17
|
+
description 'Client used to connecting to development domain'
|
18
|
+
param :development_client, instance_of(Type::GdClientType), required: true
|
19
|
+
|
20
|
+
description 'Client Used for Connecting to GD'
|
21
|
+
param :gdc_gd_client, instance_of(Type::GdClientType), required: true
|
22
|
+
|
23
|
+
description 'Logger'
|
24
|
+
param :gdc_logger, instance_of(Type::GdLogger), required: true
|
25
|
+
|
26
|
+
description 'Additional Hidden Parameters'
|
27
|
+
param :additional_hidden_params, instance_of(Type::HashType), required: false
|
28
|
+
|
29
|
+
description 'Synchronization Info'
|
30
|
+
param :synchronize, array_of(instance_of(Type::SynchronizationInfoType)), required: true, generated: true
|
31
|
+
|
32
|
+
description 'Disable synchronizing dashboard permissions for AD/KD dashboards.'
|
33
|
+
param :disable_kd_dashboard_permission, instance_of(Type::BooleanType), required: false, default: false
|
34
|
+
|
35
|
+
description 'Abort on error'
|
36
|
+
param :abort_on_error, instance_of(Type::StringType), required: false
|
37
|
+
|
38
|
+
description 'Collect synced status'
|
39
|
+
param :collect_synced_status, instance_of(Type::BooleanType), required: false
|
40
|
+
|
41
|
+
description 'Sync failed list'
|
42
|
+
param :sync_failed_list, instance_of(Type::HashType), required: false
|
43
|
+
end
|
44
|
+
|
45
|
+
class << self
|
46
|
+
def call(params)
|
47
|
+
results = ThreadSafe::Array.new
|
48
|
+
collect_synced_status = collect_synced_status(params)
|
49
|
+
failed_projects = ThreadSafe::Array.new
|
50
|
+
|
51
|
+
disable_kd_dashboard_permission = GoodData::Helpers.to_boolean(params.disable_kd_dashboard_permission)
|
52
|
+
|
53
|
+
# rubocop:disable Style/UnlessElse
|
54
|
+
unless disable_kd_dashboard_permission
|
55
|
+
dashboard_type = "KD"
|
56
|
+
dev_client = params.development_client
|
57
|
+
gdc_client = params.gdc_gd_client
|
58
|
+
|
59
|
+
params.synchronize.peach do |param_info|
|
60
|
+
from_project_info = param_info.from
|
61
|
+
to_projects_info = param_info.to
|
62
|
+
|
63
|
+
from_project = dev_client.projects(from_project_info)
|
64
|
+
unless from_project
|
65
|
+
process_failed_project(from_project_info, "Invalid 'from' project specified - '#{from_project_info}'", failed_projects, collect_synced_status)
|
66
|
+
next
|
67
|
+
end
|
68
|
+
|
69
|
+
from_dashboards = from_project.analytical_dashboards
|
70
|
+
|
71
|
+
params.gdc_logger.info "Transferring #{dashboard_type} Dashboard permission, from project: '#{from_project.title}', PID: '#{from_project.pid}' for dashboard(s): #{from_dashboards.map { |d| "#{d.title.inspect}" }.join(', ')}" # rubocop:disable Metrics/LineLength
|
72
|
+
to_projects_info.peach do |item|
|
73
|
+
to_project_pid = item[:pid]
|
74
|
+
next if sync_failed_project(to_project_pid, params)
|
75
|
+
|
76
|
+
to_project = gdc_client.projects(to_project_pid)
|
77
|
+
unless to_project
|
78
|
+
process_failed_project(to_project_pid, "Invalid 'to' project specified - '#{to_project_pid}'", failed_projects, collect_synced_status)
|
79
|
+
next
|
80
|
+
end
|
81
|
+
|
82
|
+
to_dashboards = to_project.analytical_dashboards
|
83
|
+
GoodData::Project.transfer_dashboard_permission(from_project, to_project, from_dashboards, to_dashboards)
|
84
|
+
end
|
85
|
+
|
86
|
+
results << {
|
87
|
+
from_project_name: from_project.title,
|
88
|
+
from_project_pid: from_project.pid,
|
89
|
+
status: 'ok'
|
90
|
+
}
|
91
|
+
end
|
92
|
+
else
|
93
|
+
params.gdc_logger.info "Skip synchronize KD dashboard permission."
|
94
|
+
end
|
95
|
+
|
96
|
+
process_failed_projects(failed_projects, short_name, params) if collect_synced_status
|
97
|
+
results
|
98
|
+
# rubocop:enable Style/UnlessElse
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|