gooddata 2.1.19-java → 2.3.0-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/.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
|