gooddata 2.1.19-java → 2.3.0-java
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gdc-ii-config.yaml +42 -1
- data/.github/workflows/build.yml +67 -0
- data/.github/workflows/pre-merge.yml +72 -0
- data/.pronto.yml +1 -0
- data/.rubocop.yml +2 -14
- data/CHANGELOG.md +47 -0
- data/Dockerfile +27 -14
- data/Dockerfile.jruby +5 -15
- data/Dockerfile.ruby +5 -7
- data/Gemfile +4 -2
- data/README.md +6 -6
- data/Rakefile +1 -1
- data/SDK_VERSION +1 -1
- data/VERSION +1 -1
- data/bin/run_brick.rb +7 -0
- data/ci/mssql/pom.xml +62 -0
- data/ci/mysql/pom.xml +62 -0
- data/ci/redshift/pom.xml +4 -5
- data/docker-compose.lcm.yml +42 -4
- data/docker-compose.yml +42 -0
- data/gooddata.gemspec +21 -21
- data/k8s/charts/lcm-bricks/Chart.yaml +1 -1
- data/lcm.rake +11 -8
- data/lib/gooddata/bricks/base_pipeline.rb +26 -0
- data/lib/gooddata/bricks/brick.rb +0 -1
- data/lib/gooddata/bricks/middleware/aws_middleware.rb +35 -9
- data/lib/gooddata/bricks/middleware/execution_result_middleware.rb +3 -3
- data/lib/gooddata/bricks/pipeline.rb +2 -14
- data/lib/gooddata/cloud_resources/blobstorage/blobstorage_client.rb +98 -0
- data/lib/gooddata/cloud_resources/mssql/drivers/.gitkeepme +0 -0
- data/lib/gooddata/cloud_resources/mssql/mssql_client.rb +122 -0
- data/lib/gooddata/cloud_resources/mysql/drivers/.gitkeepme +0 -0
- data/lib/gooddata/cloud_resources/mysql/mysql_client.rb +121 -0
- data/lib/gooddata/cloud_resources/postgresql/postgresql_client.rb +0 -1
- 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 +18 -1
- data/lib/gooddata/helpers/data_helper.rb +9 -4
- 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_meta.rb +3 -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 +31 -4
- 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 +112 -11
- data/lib/gooddata/lcm/actions/synchronize_dataset_mappings.rb +89 -0
- 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 +79 -23
- 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 +26 -18
- 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 +202 -0
- data/lib/gooddata/lcm/data/delete_from_lcm_release.sql.erb +5 -0
- 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/helpers/release_table_helper.rb +42 -8
- data/lib/gooddata/lcm/lcm2.rb +50 -4
- data/lib/gooddata/lcm/user_bricks_helper.rb +9 -0
- data/lib/gooddata/mixins/inspector.rb +1 -1
- data/lib/gooddata/mixins/md_object_query.rb +1 -0
- data/lib/gooddata/models/data_source.rb +5 -1
- data/lib/gooddata/models/dataset_mapping.rb +36 -0
- data/lib/gooddata/models/ldm_layout.rb +38 -0
- data/lib/gooddata/models/metadata/label.rb +26 -27
- data/lib/gooddata/models/project.rb +230 -30
- data/lib/gooddata/models/project_creator.rb +83 -6
- data/lib/gooddata/models/schedule.rb +13 -1
- data/lib/gooddata/models/segment.rb +2 -1
- data/lib/gooddata/models/user_filters/user_filter_builder.rb +162 -68
- data/lib/gooddata/rest/connection.rb +5 -3
- data/lib/gooddata/rest/phmap.rb +2 -0
- data/lib/gooddata.rb +1 -0
- data/lib/gooddata_brick_base.rb +35 -0
- data/sonar-project.properties +6 -0
- metadata +96 -65
- data/lib/gooddata/bricks/middleware/bulk_salesforce_middleware.rb +0 -37
- data/lib/gooddata/cloud_resources/redshift/drivers/log4j.properties +0 -15
@@ -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
|
@@ -5,6 +5,7 @@
|
|
5
5
|
# LICENSE file in the root directory of this source tree.
|
6
6
|
|
7
7
|
require_relative 'base_action'
|
8
|
+
require_relative '../helpers/helpers'
|
8
9
|
|
9
10
|
using TrueExtensions
|
10
11
|
using FalseExtensions
|
@@ -47,6 +48,15 @@ module GoodData
|
|
47
48
|
|
48
49
|
description 'Enables handling of deprecated objects in the logical data model.'
|
49
50
|
param :include_deprecated, instance_of(Type::BooleanType), required: false, default: false
|
51
|
+
|
52
|
+
description 'Abort on error'
|
53
|
+
param :abort_on_error, instance_of(Type::StringType), required: false
|
54
|
+
|
55
|
+
description 'Collect synced status'
|
56
|
+
param :collect_synced_status, instance_of(Type::BooleanType), required: false
|
57
|
+
|
58
|
+
description 'Sync failed list'
|
59
|
+
param :sync_failed_list, instance_of(Type::HashType), required: false
|
50
60
|
end
|
51
61
|
|
52
62
|
RESULT_HEADER = %i[from to status]
|
@@ -56,6 +66,8 @@ module GoodData
|
|
56
66
|
results = []
|
57
67
|
synchronize = []
|
58
68
|
params.synchronize.map do |segment_info|
|
69
|
+
next if sync_failed_segment(segment_info[:segment_id], params)
|
70
|
+
|
59
71
|
new_segment_info, segment_results = sync_segment_ldm(params, segment_info)
|
60
72
|
results.concat(segment_results)
|
61
73
|
synchronize << new_segment_info
|
@@ -72,69 +84,113 @@ module GoodData
|
|
72
84
|
private
|
73
85
|
|
74
86
|
def sync_segment_ldm(params, segment_info)
|
75
|
-
|
87
|
+
collect_synced_status = collect_synced_status(params)
|
88
|
+
failed_projects = ThreadSafe::Array.new
|
89
|
+
results = ThreadSafe::Array.new
|
76
90
|
client = params.gdc_gd_client
|
77
91
|
exclude_fact_rule = params.exclude_fact_rule.to_b
|
78
92
|
include_deprecated = params.include_deprecated.to_b
|
93
|
+
update_preference = params[:update_preference]
|
94
|
+
exist_fallback_to_hard_sync_config = !update_preference.nil? && !update_preference[:fallback_to_hard_sync].nil?
|
95
|
+
include_maql_fallback_hard_sync = exist_fallback_to_hard_sync_config && Helpers.to_bool('fallback_to_hard_sync', update_preference[:fallback_to_hard_sync])
|
96
|
+
|
79
97
|
from_pid = segment_info[:from]
|
80
|
-
from = params.development_client.projects(from_pid)
|
98
|
+
from = params.development_client.projects(from_pid)
|
99
|
+
unless from
|
100
|
+
segment_id = segment_info[:segment_id]
|
101
|
+
error_message = "Failed to sync LDM for segment #{segment_id}. Error: Invalid 'from' project specified - '#{from_pid}'"
|
102
|
+
fail(error_message) unless collect_synced_status
|
103
|
+
|
104
|
+
add_failed_segment(segment_id, error_message, short_name, params)
|
105
|
+
return [segment_info, results]
|
106
|
+
end
|
107
|
+
|
81
108
|
GoodData.logger.info "Creating Blueprint, project: '#{from.title}', PID: #{from_pid}"
|
82
109
|
blueprint = from.blueprint(include_ca: params.include_computed_attributes.to_b)
|
83
110
|
segment_info[:from_blueprint] = blueprint
|
84
111
|
maql_diff = nil
|
85
112
|
previous_master = segment_info[:previous_master]
|
113
|
+
synchronize_ldm_mode = params[:synchronize_ldm].downcase
|
86
114
|
diff_against_master = %w(diff_against_master_with_fallback diff_against_master)
|
87
|
-
.include?(
|
88
|
-
GoodData.logger.info "Synchronize LDM mode: '#{
|
89
|
-
if previous_master && diff_against_master
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
115
|
+
.include?(synchronize_ldm_mode)
|
116
|
+
GoodData.logger.info "Synchronize LDM mode: '#{synchronize_ldm_mode}'"
|
117
|
+
if segment_info.key?(:previous_master) && diff_against_master
|
118
|
+
if previous_master
|
119
|
+
maql_diff_params = [:includeGrain]
|
120
|
+
maql_diff_params << :excludeFactRule if exclude_fact_rule
|
121
|
+
maql_diff_params << :includeDeprecated if include_deprecated
|
122
|
+
maql_diff_params << :includeMaqlFallbackHardSync if include_maql_fallback_hard_sync
|
123
|
+
|
124
|
+
maql_diff = previous_master.maql_diff(blueprint: blueprint, params: maql_diff_params)
|
125
|
+
else
|
126
|
+
maql_diff = {
|
127
|
+
"projectModelDiff" =>
|
128
|
+
{
|
129
|
+
"updateOperations" => [],
|
130
|
+
"updateScripts" => []
|
131
|
+
}
|
132
|
+
}
|
133
|
+
end
|
134
|
+
|
94
135
|
chunks = maql_diff['projectModelDiff']['updateScripts']
|
95
136
|
if chunks.empty?
|
96
137
|
GoodData.logger.info "Synchronize LDM to clients will not proceed in mode \
|
97
|
-
'#{
|
138
|
+
'#{synchronize_ldm_mode}' due to no LDM changes in the segment master project. \
|
98
139
|
If you had changed LDM of clients manually, please use mode 'diff_against_clients' \
|
99
140
|
to force synchronize LDM to clients"
|
100
141
|
end
|
101
142
|
end
|
102
143
|
|
103
144
|
segment_info[:to] = segment_info[:to].pmap do |entry|
|
145
|
+
update_status = true
|
104
146
|
pid = entry[:pid]
|
105
|
-
|
147
|
+
next if sync_failed_project(pid, params)
|
148
|
+
|
149
|
+
to_project = client.projects(pid)
|
150
|
+
unless to_project
|
151
|
+
process_failed_project(pid, "Invalid 'to' project specified - '#{pid}'", failed_projects, collect_synced_status)
|
152
|
+
next
|
153
|
+
end
|
106
154
|
|
107
155
|
GoodData.logger.info "Updating from Blueprint, project: '#{to_project.title}', PID: #{pid}"
|
108
156
|
begin
|
109
157
|
entry[:ca_scripts] = to_project.update_from_blueprint(
|
110
158
|
blueprint,
|
111
|
-
update_preference:
|
159
|
+
update_preference: update_preference,
|
112
160
|
exclude_fact_rule: exclude_fact_rule,
|
113
161
|
execute_ca_scripts: false,
|
114
162
|
maql_diff: maql_diff,
|
115
163
|
include_deprecated: include_deprecated
|
116
164
|
)
|
117
165
|
rescue MaqlExecutionError => e
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
166
|
+
if collect_synced_status
|
167
|
+
update_status = false
|
168
|
+
failed_message = "Applying MAQL to project #{to_project.title} - #{pid} failed. Reason: #{e}"
|
169
|
+
process_failed_project(pid, failed_message, failed_projects, collect_synced_status)
|
170
|
+
else
|
171
|
+
fail e unless previous_master && params[:synchronize_ldm] == 'diff_against_master_with_fallback'
|
172
|
+
|
173
|
+
GoodData.logger.info("Restoring the client project #{to_project.title} from master.")
|
174
|
+
entry[:ca_scripts] = to_project.update_from_blueprint(
|
175
|
+
blueprint,
|
176
|
+
update_preference: update_preference,
|
177
|
+
exclude_fact_rule: exclude_fact_rule,
|
178
|
+
execute_ca_scripts: false,
|
179
|
+
include_deprecated: include_deprecated
|
180
|
+
)
|
181
|
+
end
|
128
182
|
end
|
129
183
|
|
130
184
|
results << {
|
131
185
|
from: from_pid,
|
132
186
|
to: pid,
|
133
187
|
status: 'ok'
|
134
|
-
}
|
188
|
+
} if update_status
|
189
|
+
|
135
190
|
entry
|
136
191
|
end
|
137
192
|
|
193
|
+
process_failed_projects(failed_projects, short_name, params) if collect_synced_status
|
138
194
|
[segment_info, results]
|
139
195
|
end
|
140
196
|
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
#
|
4
|
+
# Copyright (c) 2022 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
|
+
|
10
|
+
module GoodData
|
11
|
+
module LCM2
|
12
|
+
class SynchronizeLdmLayout < BaseAction
|
13
|
+
DESCRIPTION = 'Synchronize LDM Layout'
|
14
|
+
|
15
|
+
PARAMS = define_params(self) do
|
16
|
+
description 'Logger'
|
17
|
+
param :gdc_logger, instance_of(Type::GdLogger), required: true
|
18
|
+
|
19
|
+
description 'Client Used for Connecting to GD'
|
20
|
+
param :gdc_gd_client, instance_of(Type::GdClientType), required: true
|
21
|
+
|
22
|
+
description 'Client used to connecting to development domain'
|
23
|
+
param :development_client, instance_of(Type::GdClientType), required: true
|
24
|
+
|
25
|
+
description 'Synchronization Info'
|
26
|
+
param :synchronize, array_of(instance_of(Type::SynchronizationInfoType)), required: true, generated: 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
|
36
|
+
end
|
37
|
+
|
38
|
+
RESULT_HEADER = %i[from to status]
|
39
|
+
|
40
|
+
class << self
|
41
|
+
def call(params)
|
42
|
+
results = []
|
43
|
+
|
44
|
+
client = params.gdc_gd_client
|
45
|
+
development_client = params.development_client
|
46
|
+
gdc_logger = params.gdc_logger
|
47
|
+
collect_synced_status = collect_synced_status(params)
|
48
|
+
failed_projects = ThreadSafe::Array.new
|
49
|
+
|
50
|
+
params.synchronize.peach do |info|
|
51
|
+
from_project = info.from
|
52
|
+
to_projects = info.to
|
53
|
+
|
54
|
+
from = development_client.projects(from_project)
|
55
|
+
unless from
|
56
|
+
process_failed_project(from_project, "Invalid 'from' project specified - '#{from_project}'", failed_projects, collect_synced_status)
|
57
|
+
next
|
58
|
+
end
|
59
|
+
|
60
|
+
from_pid = from.pid
|
61
|
+
from_title = from.title
|
62
|
+
from_ldm_layout = from.ldm_layout
|
63
|
+
|
64
|
+
if from_ldm_layout&.dig('ldmLayout', 'layout').nil? || from_ldm_layout['ldmLayout']['layout'].empty?
|
65
|
+
gdc_logger.info "Project: '#{from_title}', PID: '#{from_pid}' has no ldm layout, skip synchronizing ldm layout."
|
66
|
+
else
|
67
|
+
to_projects.peach do |to|
|
68
|
+
pid = to[:pid]
|
69
|
+
to_project = client.projects(pid)
|
70
|
+
unless to_project
|
71
|
+
process_failed_project(pid, "Invalid 'from' project specified - '#{pid}'", failed_projects, collect_synced_status)
|
72
|
+
next
|
73
|
+
end
|
74
|
+
|
75
|
+
next if sync_failed_project(pid, params)
|
76
|
+
|
77
|
+
gdc_logger.info "Transferring ldm layout, from project: '#{from_title}', PID: '#{from_pid}', to project: '#{to_project.title}', PID: '#{to_project.pid}'"
|
78
|
+
res = to_project.save_ldm_layout(from_ldm_layout)
|
79
|
+
|
80
|
+
if res[:status] == 'OK' || !collect_synced_status
|
81
|
+
res[:from] = from_pid
|
82
|
+
results << res
|
83
|
+
else
|
84
|
+
warning_message = "Failed to transfer ldm layout from project: '#{from_pid}' to project: '#{to_project.title}'"
|
85
|
+
process_failed_project(pid, warning_message, failed_projects, collect_synced_status)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
process_failed_projects(failed_projects, short_name, params) if collect_synced_status
|
92
|
+
# Return results
|
93
|
+
results.flatten
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,108 @@
|
|
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
|
+
|
10
|
+
require 'thread_safe'
|
11
|
+
require 'json'
|
12
|
+
|
13
|
+
module GoodData
|
14
|
+
module LCM2
|
15
|
+
class SynchronizePPDashboardPermissions < BaseAction
|
16
|
+
DESCRIPTION = 'Synchronize Pixel Perfect Dashboard Permission'
|
17
|
+
|
18
|
+
PARAMS = define_params(self) do
|
19
|
+
description 'Client Used for Connecting to GD'
|
20
|
+
param :gdc_gd_client, instance_of(Type::GdClientType), required: true
|
21
|
+
|
22
|
+
description 'Client used to connecting to development domain'
|
23
|
+
param :development_client, instance_of(Type::GdClientType), required: true
|
24
|
+
|
25
|
+
description 'Synchronization Info'
|
26
|
+
param :synchronize, array_of(instance_of(Type::SynchronizationInfoType)), required: true, generated: true
|
27
|
+
|
28
|
+
description 'Logger'
|
29
|
+
param :gdc_logger, instance_of(Type::GdLogger), required: true
|
30
|
+
|
31
|
+
description 'Additional Hidden Parameters'
|
32
|
+
param :additional_hidden_params, instance_of(Type::HashType), required: false
|
33
|
+
|
34
|
+
description 'Disable synchronizing dashboard permissions for Pixel Perfect dashboards'
|
35
|
+
param :disable_pp_dashboard_permission, instance_of(Type::BooleanType), required: false, default: false
|
36
|
+
|
37
|
+
description 'Abort on error'
|
38
|
+
param :abort_on_error, instance_of(Type::StringType), required: false
|
39
|
+
|
40
|
+
description 'Collect synced status'
|
41
|
+
param :collect_synced_status, instance_of(Type::BooleanType), required: false
|
42
|
+
|
43
|
+
description 'Sync failed list'
|
44
|
+
param :sync_failed_list, instance_of(Type::HashType), required: false
|
45
|
+
end
|
46
|
+
|
47
|
+
class << self
|
48
|
+
def call(params)
|
49
|
+
results = []
|
50
|
+
disable_pp_dashboard_permission = GoodData::Helpers.to_boolean(params.disable_pp_dashboard_permission)
|
51
|
+
collect_synced_status = collect_synced_status(params)
|
52
|
+
failed_projects = ThreadSafe::Array.new
|
53
|
+
|
54
|
+
if disable_pp_dashboard_permission
|
55
|
+
params.gdc_logger.info "Skip synchronize Pixel Perfect dashboard permission."
|
56
|
+
else
|
57
|
+
client = params.gdc_gd_client
|
58
|
+
development_client = params.development_client
|
59
|
+
failed_projects = ThreadSafe::Array.new
|
60
|
+
|
61
|
+
params.synchronize.peach do |info|
|
62
|
+
from_project = info.from
|
63
|
+
to_projects = info.to
|
64
|
+
sync_success = false
|
65
|
+
|
66
|
+
from = development_client.projects(from_project)
|
67
|
+
unless from
|
68
|
+
process_failed_project(from_project, "Invalid 'from' project specified - '#{from_project}'", failed_projects, collect_synced_status)
|
69
|
+
next
|
70
|
+
end
|
71
|
+
|
72
|
+
source_dashboards = from.dashboards
|
73
|
+
|
74
|
+
params.gdc_logger.info "Transferring Pixel Perfect Dashboard permission, from project: '#{from.title}', PID: '#{from.pid}' for dashboard(s): #{source_dashboards.map { |d| "#{d.title.inspect}" }.join(', ')}" # rubocop:disable Metrics/LineLength
|
75
|
+
to_projects.peach do |entry|
|
76
|
+
pid = entry[:pid]
|
77
|
+
next if sync_failed_project(pid, params)
|
78
|
+
|
79
|
+
to_project = client.projects(pid)
|
80
|
+
unless to_project
|
81
|
+
process_failed_project(pid, "Invalid 'to' project specified - '#{pid}'", failed_projects, collect_synced_status)
|
82
|
+
next
|
83
|
+
end
|
84
|
+
|
85
|
+
target_dashboards = to_project.dashboards
|
86
|
+
begin
|
87
|
+
GoodData::Project.transfer_dashboard_permission(from, to_project, source_dashboards, target_dashboards)
|
88
|
+
sync_success = true
|
89
|
+
rescue StandardError => err
|
90
|
+
process_failed_project(pid, err.message, failed_projects, collect_synced_status)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
results << {
|
95
|
+
from_project_name: from.title,
|
96
|
+
from_project_pid: from.pid,
|
97
|
+
status: 'ok'
|
98
|
+
} if sync_success
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
process_failed_projects(failed_projects, short_name, params) if collect_synced_status
|
103
|
+
results
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -61,12 +61,13 @@ module GoodData
|
|
61
61
|
to_projects = info.to
|
62
62
|
|
63
63
|
from = development_client.projects(from_project) || fail("Invalid 'from' project specified - '#{from_project}'")
|
64
|
+
has_cycle_trigger = exist_cycle_trigger(from)
|
64
65
|
to_projects.peach do |entry|
|
65
66
|
pid = entry[:pid]
|
66
67
|
to_project = client.projects(pid) || fail("Invalid 'to' project specified - '#{pid}'")
|
67
68
|
|
68
69
|
params.gdc_logger.info "Transferring Schedules, from project: '#{from.title}', PID: '#{from.pid}', to project: '#{to_project.title}', PID: '#{to_project.pid}'"
|
69
|
-
res = GoodData::Project.transfer_schedules(from, to_project).sort_by do |item|
|
70
|
+
res = GoodData::Project.transfer_schedules(from, to_project, has_cycle_trigger).sort_by do |item|
|
70
71
|
item[:status]
|
71
72
|
end
|
72
73
|
|
@@ -97,6 +98,35 @@ module GoodData
|
|
97
98
|
# Return results
|
98
99
|
results
|
99
100
|
end
|
101
|
+
|
102
|
+
private
|
103
|
+
|
104
|
+
def exist_cycle_trigger(project)
|
105
|
+
schedules = project.schedules
|
106
|
+
triggers = {}
|
107
|
+
schedules.each do |schedule|
|
108
|
+
triggers[schedule.obj_id] = schedule.trigger_id if schedule.trigger_id
|
109
|
+
end
|
110
|
+
|
111
|
+
triggers.each do |schedule_id, trigger_id|
|
112
|
+
checking_id = trigger_id
|
113
|
+
if checking_id == schedule_id
|
114
|
+
return true
|
115
|
+
else
|
116
|
+
max_step = triggers.length
|
117
|
+
count = 1
|
118
|
+
loop do
|
119
|
+
checking_id = triggers[checking_id]
|
120
|
+
count += 1
|
121
|
+
break if !checking_id || count > max_step
|
122
|
+
|
123
|
+
return true if checking_id == schedule_id
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
false
|
129
|
+
end
|
100
130
|
end
|
101
131
|
end
|
102
132
|
end
|