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.
Files changed (85) hide show
  1. checksums.yaml +4 -4
  2. data/.gdc-ii-config.yaml +42 -1
  3. data/.github/workflows/build.yml +67 -0
  4. data/.github/workflows/pre-merge.yml +72 -0
  5. data/.pronto.yml +1 -0
  6. data/.rubocop.yml +2 -14
  7. data/CHANGELOG.md +47 -0
  8. data/Dockerfile +27 -14
  9. data/Dockerfile.jruby +5 -15
  10. data/Dockerfile.ruby +5 -7
  11. data/Gemfile +4 -2
  12. data/README.md +6 -6
  13. data/Rakefile +1 -1
  14. data/SDK_VERSION +1 -1
  15. data/VERSION +1 -1
  16. data/bin/run_brick.rb +7 -0
  17. data/ci/mssql/pom.xml +62 -0
  18. data/ci/mysql/pom.xml +62 -0
  19. data/ci/redshift/pom.xml +4 -5
  20. data/docker-compose.lcm.yml +42 -4
  21. data/docker-compose.yml +42 -0
  22. data/gooddata.gemspec +21 -21
  23. data/k8s/charts/lcm-bricks/Chart.yaml +1 -1
  24. data/lcm.rake +11 -8
  25. data/lib/gooddata/bricks/base_pipeline.rb +26 -0
  26. data/lib/gooddata/bricks/brick.rb +0 -1
  27. data/lib/gooddata/bricks/middleware/aws_middleware.rb +35 -9
  28. data/lib/gooddata/bricks/middleware/execution_result_middleware.rb +3 -3
  29. data/lib/gooddata/bricks/pipeline.rb +2 -14
  30. data/lib/gooddata/cloud_resources/blobstorage/blobstorage_client.rb +98 -0
  31. data/lib/gooddata/cloud_resources/mssql/drivers/.gitkeepme +0 -0
  32. data/lib/gooddata/cloud_resources/mssql/mssql_client.rb +122 -0
  33. data/lib/gooddata/cloud_resources/mysql/drivers/.gitkeepme +0 -0
  34. data/lib/gooddata/cloud_resources/mysql/mysql_client.rb +121 -0
  35. data/lib/gooddata/cloud_resources/postgresql/postgresql_client.rb +0 -1
  36. data/lib/gooddata/cloud_resources/redshift/drivers/.gitkeepme +0 -0
  37. data/lib/gooddata/cloud_resources/redshift/redshift_client.rb +0 -2
  38. data/lib/gooddata/cloud_resources/snowflake/snowflake_client.rb +18 -1
  39. data/lib/gooddata/helpers/data_helper.rb +9 -4
  40. data/lib/gooddata/lcm/actions/base_action.rb +157 -0
  41. data/lib/gooddata/lcm/actions/collect_data_product.rb +2 -1
  42. data/lib/gooddata/lcm/actions/collect_meta.rb +3 -1
  43. data/lib/gooddata/lcm/actions/collect_projects_warning_status.rb +53 -0
  44. data/lib/gooddata/lcm/actions/collect_segment_clients.rb +14 -0
  45. data/lib/gooddata/lcm/actions/initialize_continue_on_error_option.rb +87 -0
  46. data/lib/gooddata/lcm/actions/migrate_gdc_date_dimension.rb +31 -4
  47. data/lib/gooddata/lcm/actions/provision_clients.rb +34 -5
  48. data/lib/gooddata/lcm/actions/synchronize_cas.rb +24 -4
  49. data/lib/gooddata/lcm/actions/synchronize_clients.rb +112 -11
  50. data/lib/gooddata/lcm/actions/synchronize_dataset_mappings.rb +89 -0
  51. data/lib/gooddata/lcm/actions/synchronize_etls_in_segment.rb +48 -11
  52. data/lib/gooddata/lcm/actions/synchronize_kd_dashboard_permission.rb +103 -0
  53. data/lib/gooddata/lcm/actions/synchronize_ldm.rb +79 -23
  54. data/lib/gooddata/lcm/actions/synchronize_ldm_layout.rb +98 -0
  55. data/lib/gooddata/lcm/actions/synchronize_pp_dashboard_permission.rb +108 -0
  56. data/lib/gooddata/lcm/actions/synchronize_schedules.rb +31 -1
  57. data/lib/gooddata/lcm/actions/synchronize_user_filters.rb +26 -18
  58. data/lib/gooddata/lcm/actions/synchronize_user_groups.rb +30 -4
  59. data/lib/gooddata/lcm/actions/synchronize_users.rb +11 -10
  60. data/lib/gooddata/lcm/actions/update_metric_formats.rb +202 -0
  61. data/lib/gooddata/lcm/data/delete_from_lcm_release.sql.erb +5 -0
  62. data/lib/gooddata/lcm/exceptions/lcm_execution_warning.rb +15 -0
  63. data/lib/gooddata/lcm/helpers/check_helper.rb +19 -0
  64. data/lib/gooddata/lcm/helpers/release_table_helper.rb +42 -8
  65. data/lib/gooddata/lcm/lcm2.rb +50 -4
  66. data/lib/gooddata/lcm/user_bricks_helper.rb +9 -0
  67. data/lib/gooddata/mixins/inspector.rb +1 -1
  68. data/lib/gooddata/mixins/md_object_query.rb +1 -0
  69. data/lib/gooddata/models/data_source.rb +5 -1
  70. data/lib/gooddata/models/dataset_mapping.rb +36 -0
  71. data/lib/gooddata/models/ldm_layout.rb +38 -0
  72. data/lib/gooddata/models/metadata/label.rb +26 -27
  73. data/lib/gooddata/models/project.rb +230 -30
  74. data/lib/gooddata/models/project_creator.rb +83 -6
  75. data/lib/gooddata/models/schedule.rb +13 -1
  76. data/lib/gooddata/models/segment.rb +2 -1
  77. data/lib/gooddata/models/user_filters/user_filter_builder.rb +162 -68
  78. data/lib/gooddata/rest/connection.rb +5 -3
  79. data/lib/gooddata/rest/phmap.rb +2 -0
  80. data/lib/gooddata.rb +1 -0
  81. data/lib/gooddata_brick_base.rb +35 -0
  82. data/sonar-project.properties +6 -0
  83. metadata +96 -65
  84. data/lib/gooddata/bricks/middleware/bulk_salesforce_middleware.rb +0 -37
  85. 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
- fail "Failed to sync processes/schedules for segment #{segment_id}. Error: #{res[:syncedResult][:errors].pretty_inspect}"
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
- to_project = client.projects(pid) || fail("Invalid 'to' project specified - '#{pid}'")
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
- unless from_project_etl_names[:schedules].include?([schedule.name, to_project_process_id_names[schedule.process_id]])
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(params_for_all_projects[schedule.name]) if params_for_all_projects[schedule.name]
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(params_for_this_client[schedule.name]) if params_for_this_client[schedule.name]
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(hidden_params_for_all_projects[schedule.name]) if hidden_params_for_all_projects[schedule.name]
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(hidden_params_for_this_client[schedule.name]) if hidden_params_for_this_client[schedule.name]
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) || fail("Invalid 'from' project specified - '#{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
- results = []
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) || fail("Invalid 'from' project specified - '#{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?(params[:synchronize_ldm].downcase)
88
- GoodData.logger.info "Synchronize LDM mode: '#{params[:synchronize_ldm].downcase}'"
89
- if previous_master && diff_against_master
90
- maql_diff_params = [:includeGrain]
91
- maql_diff_params << :excludeFactRule if exclude_fact_rule
92
- maql_diff_params << :includeDeprecated if include_deprecated
93
- maql_diff = previous_master.maql_diff(blueprint: blueprint, params: maql_diff_params)
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
- '#{params[:synchronize_ldm].downcase}' due to no LDM changes in the new master project. \
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
- to_project = client.projects(pid) || fail("Invalid 'to' project specified - '#{pid}'")
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: params[: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
- GoodData.logger.info("Applying MAQL to project #{to_project.title} - #{pid} failed. Reason: #{e}")
119
- fail e unless previous_master && params[:synchronize_ldm] == 'diff_against_master_with_fallback'
120
- GoodData.logger.info("Restoring the client project #{to_project.title} from master.")
121
- entry[:ca_scripts] = to_project.update_from_blueprint(
122
- blueprint,
123
- update_preference: params[:update_preference],
124
- exclude_fact_rule: exclude_fact_rule,
125
- execute_ca_scripts: false,
126
- include_deprecated: include_deprecated
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