gooddata 2.1.19 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) 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/LICENSE +4409 -16
  13. data/README.md +6 -6
  14. data/Rakefile +1 -1
  15. data/SDK_VERSION +1 -1
  16. data/VERSION +1 -1
  17. data/bin/run_brick.rb +7 -0
  18. data/ci/mssql/pom.xml +62 -0
  19. data/ci/mysql/pom.xml +62 -0
  20. data/ci/redshift/pom.xml +4 -5
  21. data/docker-compose.lcm.yml +42 -4
  22. data/docker-compose.yml +42 -0
  23. data/gooddata.gemspec +21 -21
  24. data/k8s/charts/lcm-bricks/Chart.yaml +1 -1
  25. data/lcm.rake +11 -8
  26. data/lib/gooddata/bricks/base_pipeline.rb +26 -0
  27. data/lib/gooddata/bricks/brick.rb +0 -1
  28. data/lib/gooddata/bricks/middleware/aws_middleware.rb +35 -9
  29. data/lib/gooddata/bricks/middleware/execution_result_middleware.rb +3 -3
  30. data/lib/gooddata/bricks/pipeline.rb +2 -14
  31. data/lib/gooddata/cloud_resources/blobstorage/blobstorage_client.rb +98 -0
  32. data/lib/gooddata/cloud_resources/mssql/drivers/.gitkeepme +0 -0
  33. data/lib/gooddata/cloud_resources/mssql/mssql_client.rb +122 -0
  34. data/lib/gooddata/cloud_resources/mysql/drivers/.gitkeepme +0 -0
  35. data/lib/gooddata/cloud_resources/mysql/mysql_client.rb +121 -0
  36. data/lib/gooddata/cloud_resources/postgresql/postgresql_client.rb +0 -1
  37. data/lib/gooddata/cloud_resources/redshift/drivers/.gitkeepme +0 -0
  38. data/lib/gooddata/cloud_resources/redshift/redshift_client.rb +0 -2
  39. data/lib/gooddata/cloud_resources/snowflake/snowflake_client.rb +18 -1
  40. data/lib/gooddata/helpers/data_helper.rb +9 -4
  41. data/lib/gooddata/lcm/actions/base_action.rb +157 -0
  42. data/lib/gooddata/lcm/actions/collect_data_product.rb +2 -1
  43. data/lib/gooddata/lcm/actions/collect_meta.rb +3 -1
  44. data/lib/gooddata/lcm/actions/collect_projects_warning_status.rb +53 -0
  45. data/lib/gooddata/lcm/actions/collect_segment_clients.rb +14 -0
  46. data/lib/gooddata/lcm/actions/initialize_continue_on_error_option.rb +87 -0
  47. data/lib/gooddata/lcm/actions/migrate_gdc_date_dimension.rb +31 -4
  48. data/lib/gooddata/lcm/actions/provision_clients.rb +34 -5
  49. data/lib/gooddata/lcm/actions/synchronize_cas.rb +24 -4
  50. data/lib/gooddata/lcm/actions/synchronize_clients.rb +112 -11
  51. data/lib/gooddata/lcm/actions/synchronize_dataset_mappings.rb +89 -0
  52. data/lib/gooddata/lcm/actions/synchronize_etls_in_segment.rb +48 -11
  53. data/lib/gooddata/lcm/actions/synchronize_kd_dashboard_permission.rb +103 -0
  54. data/lib/gooddata/lcm/actions/synchronize_ldm.rb +79 -23
  55. data/lib/gooddata/lcm/actions/synchronize_ldm_layout.rb +98 -0
  56. data/lib/gooddata/lcm/actions/synchronize_pp_dashboard_permission.rb +108 -0
  57. data/lib/gooddata/lcm/actions/synchronize_schedules.rb +31 -1
  58. data/lib/gooddata/lcm/actions/synchronize_user_filters.rb +26 -18
  59. data/lib/gooddata/lcm/actions/synchronize_user_groups.rb +30 -4
  60. data/lib/gooddata/lcm/actions/synchronize_users.rb +11 -10
  61. data/lib/gooddata/lcm/actions/update_metric_formats.rb +202 -0
  62. data/lib/gooddata/lcm/data/delete_from_lcm_release.sql.erb +5 -0
  63. data/lib/gooddata/lcm/exceptions/lcm_execution_warning.rb +15 -0
  64. data/lib/gooddata/lcm/helpers/check_helper.rb +19 -0
  65. data/lib/gooddata/lcm/helpers/release_table_helper.rb +42 -8
  66. data/lib/gooddata/lcm/lcm2.rb +50 -4
  67. data/lib/gooddata/lcm/user_bricks_helper.rb +9 -0
  68. data/lib/gooddata/mixins/inspector.rb +1 -1
  69. data/lib/gooddata/mixins/md_object_query.rb +1 -0
  70. data/lib/gooddata/models/data_source.rb +5 -1
  71. data/lib/gooddata/models/dataset_mapping.rb +36 -0
  72. data/lib/gooddata/models/ldm_layout.rb +38 -0
  73. data/lib/gooddata/models/metadata/label.rb +26 -27
  74. data/lib/gooddata/models/project.rb +230 -30
  75. data/lib/gooddata/models/project_creator.rb +83 -6
  76. data/lib/gooddata/models/schedule.rb +13 -1
  77. data/lib/gooddata/models/segment.rb +2 -1
  78. data/lib/gooddata/models/user_filters/user_filter_builder.rb +162 -68
  79. data/lib/gooddata/rest/connection.rb +5 -3
  80. data/lib/gooddata/rest/phmap.rb +2 -0
  81. data/lib/gooddata.rb +1 -0
  82. data/lib/gooddata_brick_base.rb +35 -0
  83. data/sonar-project.properties +6 -0
  84. metadata +100 -68
  85. data/lib/gooddata/bricks/middleware/bulk_salesforce_middleware.rb +0 -37
  86. data/lib/gooddata/cloud_resources/redshift/drivers/log4j.properties +0 -15
@@ -17,6 +17,7 @@ require_relative 'actions/actions'
17
17
  require_relative 'dsl/dsl'
18
18
  require_relative 'helpers/helpers'
19
19
  require_relative 'exceptions/lcm_execution_error'
20
+ require_relative 'exceptions/lcm_execution_warning'
20
21
 
21
22
  using TrueExtensions
22
23
  using FalseExtensions
@@ -96,10 +97,14 @@ module GoodData
96
97
  CollectComputedAttributeMetrics,
97
98
  ImportObjectCollections,
98
99
  SynchronizeComputedAttributes,
100
+ SynchronizeDataSetMapping,
101
+ SynchronizeLdmLayout,
99
102
  SynchronizeProcesses,
100
103
  SynchronizeSchedules,
101
104
  SynchronizeColorPalette,
102
105
  SynchronizeUserGroups,
106
+ SynchronizePPDashboardPermissions,
107
+ SynchronizeKDDashboardPermissions,
103
108
  SynchronizeNewSegments,
104
109
  UpdateReleaseTable
105
110
  ],
@@ -121,11 +126,19 @@ module GoodData
121
126
  CollectClients,
122
127
  AssociateClients,
123
128
  RenameExistingClientProjects,
129
+ InitializeContinueOnErrorOption,
124
130
  ProvisionClients,
131
+ UpdateMetricFormats,
125
132
  EnsureTechnicalUsersDomain,
126
133
  EnsureTechnicalUsersProject,
127
134
  CollectDymanicScheduleParams,
128
- SynchronizeETLsInSegment
135
+ SynchronizeDataSetMapping,
136
+ SynchronizeLdmLayout,
137
+ SynchronizeUserGroups,
138
+ SynchronizePPDashboardPermissions,
139
+ SynchronizeKDDashboardPermissions,
140
+ SynchronizeETLsInSegment,
141
+ CollectProjectsWarningStatus
129
142
  ],
130
143
 
131
144
  rollout: [
@@ -135,12 +148,20 @@ module GoodData
135
148
  CollectSegmentClients,
136
149
  EnsureTechnicalUsersDomain,
137
150
  EnsureTechnicalUsersProject,
151
+ InitializeContinueOnErrorOption,
138
152
  SynchronizeLdm,
153
+ SynchronizeDataSetMapping,
154
+ SynchronizeLdmLayout,
139
155
  MigrateGdcDateDimension,
140
156
  SynchronizeClients,
157
+ SynchronizeUserGroups,
158
+ SynchronizePPDashboardPermissions,
159
+ SynchronizeKDDashboardPermissions,
160
+ UpdateMetricFormats,
141
161
  SynchronizeComputedAttributes,
142
162
  CollectDymanicScheduleParams,
143
- SynchronizeETLsInSegment
163
+ SynchronizeETLsInSegment,
164
+ CollectProjectsWarningStatus
144
165
  ],
145
166
 
146
167
  users: [
@@ -335,7 +356,7 @@ module GoodData
335
356
  # Invoke action
336
357
  begin
337
358
  out = run_action action, params
338
- rescue Exception => e # rubocop:disable RescueException
359
+ rescue Exception => e # rubocop:disable Style/RescueException
339
360
  errors << {
340
361
  action: action,
341
362
  err: e,
@@ -357,7 +378,7 @@ module GoodData
357
378
  params.merge!(new_params)
358
379
 
359
380
  # Print action result
360
- print_action_result(action, res)
381
+ print_action_result(action, res) if action.send(:print_result, params)
361
382
 
362
383
  # Store result for final summary
363
384
  results << res
@@ -384,9 +405,28 @@ module GoodData
384
405
  end
385
406
  end
386
407
 
408
+ process_sync_failed_projects(params) if GoodData::LCM2::Helpers.collect_synced_status(params) && strict_mode
409
+
387
410
  result
388
411
  end
389
412
 
413
+ def process_sync_failed_projects(params)
414
+ sync_failed_list = params[:sync_failed_list]
415
+ sync_project_list = sync_failed_list[:project_client_mappings]
416
+ sync_failed_project_list = sync_failed_list[:failed_detailed_projects]
417
+
418
+ if sync_project_list && sync_failed_project_list && sync_project_list.size.positive? && sync_failed_project_list.size.positive?
419
+ failed_project = sync_failed_project_list[0]
420
+ summary_message = "Existing errors during execution. See log for details"
421
+ error_message = failed_project[:message]
422
+ if sync_project_list.size == sync_failed_project_list.size
423
+ raise GoodData::LcmExecutionError.new(summary_message, error_message)
424
+ else
425
+ raise GoodData::LcmExecutionWarning.new(summary_message, error_message)
426
+ end
427
+ end
428
+ end
429
+
390
430
  def run_action(action, params)
391
431
  begin
392
432
  GoodData.gd_logger.start_action action, GoodData.gd_logger
@@ -396,6 +436,12 @@ module GoodData
396
436
  BaseAction.check_params(action.const_get('PARAMS'), params)
397
437
  params.setup_filters(action.const_get('PARAMS'))
398
438
  out = action.send(:call, params)
439
+ rescue Exception => e # rubocop:disable Style/RescueException
440
+ # Log to splunk
441
+ GoodData.gd_logger.error("action=#{action} status=failed message=#{e} exception=#{e.backtrace}")
442
+ # Log to execution log
443
+ GoodData.logger.error("Execution #{action} failed. Error: #{e}. Detail:#{e.backtrace}")
444
+ raise e
399
445
  ensure
400
446
  params.clear_filters
401
447
  GoodData.gd_logger.end_action GoodData.gd_logger
@@ -26,6 +26,15 @@ module GoodData
26
26
 
27
27
  goodot_id.empty? ? client.id : goodot_id
28
28
  end
29
+
30
+ def non_working_clients(domain_clients, working_client_ids)
31
+ non_working_clients = []
32
+ domain_clients.each do |c|
33
+ non_working_clients << c unless working_client_ids.include?(c.client_id.to_s)
34
+ end
35
+
36
+ non_working_clients
37
+ end
29
38
  end
30
39
  end
31
40
  end
@@ -10,7 +10,7 @@ module GoodData
10
10
  module Mixin
11
11
  # When an RSpec test like this fails,
12
12
  #
13
- # @my_array.should == [@some_model, @some_model2]
13
+ # expect(@my_array).to == [@some_model, @some_model2]
14
14
  #
15
15
  # RSpec will call inspect on each of the objects to "help" you figure out
16
16
  # what went wrong. Well, inspect will usually dump a TON OF SHIT and make trying
@@ -54,6 +54,7 @@ module GoodData
54
54
  y << (klass ? client.create(klass, item, project: project) : item)
55
55
  end
56
56
  break if result['objects']['paging']['count'] < page_limit
57
+
57
58
  offset += page_limit
58
59
  end
59
60
  end
@@ -34,7 +34,7 @@ module GoodData
34
34
  c.create(DataSource, ds_data)
35
35
  end
36
36
  else
37
- c.create(DataSource, c.get("#{DATA_SOURCES_URL}/#{id}"))
37
+ c.create(DataSource, c.get(DATA_SOURCES_URL + '/' + id))
38
38
  end
39
39
  end
40
40
 
@@ -177,6 +177,10 @@ module GoodData
177
177
  @json['dataSource']['connectionInfo'][type]
178
178
  end
179
179
 
180
+ def type
181
+ @json['dataSource']['connectionInfo'].first[0].upcase
182
+ end
183
+
180
184
  private
181
185
 
182
186
  def build_connection_info
@@ -0,0 +1,36 @@
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
+ module GoodData
9
+ class DatasetMapping
10
+ DATASET_MAPPING_GET_URI = '/gdc/dataload/projects/%<project_id>s/modelMapping/datasets'
11
+ DATASET_MAPPING_UPDATE_URI = '/gdc/dataload/projects/%<project_id>s/modelMapping/datasets/bulk/upsert'
12
+
13
+ class << self
14
+ def [](opts = { :client => GoodData.connection, :project => GoodData.project })
15
+ client, project = GoodData.get_client_and_project(opts)
16
+ get_uri = DATASET_MAPPING_GET_URI % { project_id: project.pid }
17
+ res = client.get(get_uri)
18
+ res
19
+ end
20
+
21
+ alias_method :get, :[]
22
+ end
23
+
24
+ def initialize(data)
25
+ @data = data
26
+ end
27
+
28
+ def save(opts)
29
+ client, project = GoodData.get_client_and_project(opts)
30
+
31
+ post_uri = DATASET_MAPPING_UPDATE_URI % { project_id: project.pid }
32
+ res = client.post(post_uri, @data, opts)
33
+ res
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,38 @@
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
+ module GoodData
9
+ class LdmLayout
10
+ DEFAULT_EMPTY_LDM_LAYOUT = {
11
+ "ldmLayout" => {
12
+ "layout" => []
13
+ }
14
+ }
15
+
16
+ LDM_LAYOUT_URI = '/gdc/dataload/internal/projects/%<project_id>s/ldmLayout'
17
+
18
+ class << self
19
+ def get(opts = { :client => GoodData.connection, :project => GoodData.project })
20
+ client, project = GoodData.get_client_and_project(opts)
21
+ get_uri = LDM_LAYOUT_URI % { project_id: project.pid }
22
+
23
+ client.get(get_uri)
24
+ end
25
+ end
26
+
27
+ def initialize(data)
28
+ @data = data
29
+ end
30
+
31
+ def save(opts)
32
+ client, project = GoodData.get_client_and_project(opts)
33
+ post_uri = LDM_LAYOUT_URI % { project_id: project.pid }
34
+
35
+ client.post(post_uri, @data, opts)
36
+ end
37
+ end
38
+ end
@@ -40,22 +40,20 @@ module GoodData
40
40
  end
41
41
  end
42
42
 
43
- # Gets valid elements using /validElements? API
43
+ # Gets valid elements of a label for a specific paging (:offset and :limit) or get validElements of a specific value (:filter).
44
+ # In the case filter a specific value, because the API /validElements only filter by partial match, we need to filter again at client side for exact match.
44
45
  # @return [Array] Results
45
46
  def get_valid_elements(*args)
46
- results, params = valid_elements(*args)
47
- # TMA-775 - the validElements API can possibly return more matches than requested (usually 1)
48
- # so we do a preliminary first request to check and then increase the limit if needed
49
- if results['validElements']['paging']['total'].to_i != params[:limit]
47
+ if args && !args.empty? && args.first[:filter]
48
+ params = args.first
50
49
  params[:limit] = 100_000
51
50
  results, = valid_elements params
52
- if params[:filter]
53
- results['validElements']['items'] = results['validElements']['items'].reject do |i|
54
- i['element']['title'] != params[:filter]
55
- end
51
+ results['validElements']['items'] = results['validElements']['items'].select do |i|
52
+ i['element']['title'] == params[:filter]
56
53
  end
54
+ else
55
+ results, = valid_elements(*args)
57
56
  end
58
-
59
57
  results
60
58
  end
61
59
 
@@ -74,24 +72,25 @@ module GoodData
74
72
  # @option options [Number] :limit limits the number of values to certain number. Default is 100
75
73
  # @return [Array]
76
74
  def values(options = {})
77
- Enumerator.new do |y|
78
- offset = options[:offset] || 0
79
- page_limit = options[:limit] || 100
80
- loop do
81
- results = get_valid_elements(limit: page_limit, offset: offset)
82
-
83
- elements = results['validElements']
84
- elements['items'].map do |el|
85
- v = el['element']
86
- y << {
87
- :value => v['title'],
88
- :uri => v['uri']
89
- }
90
- end
91
- break if elements['items'].count < page_limit
92
- offset += page_limit
75
+ all_values = []
76
+ offset = options[:offset] || 0
77
+ page_limit = options[:limit] || 100
78
+ loop do
79
+ results = get_valid_elements(limit: page_limit, offset: offset)
80
+
81
+ elements = results['validElements']
82
+ elements['items'].map do |el|
83
+ v = el['element']
84
+ all_values << {
85
+ :value => v['title'],
86
+ :uri => v['uri']
87
+ }
93
88
  end
89
+ break if elements['items'].count < page_limit
90
+
91
+ offset += page_limit
94
92
  end
93
+ all_values
95
94
  end
96
95
 
97
96
  def values_count
@@ -136,7 +135,7 @@ module GoodData
136
135
  if status_url
137
136
  results = client.poll_on_response(status_url) do |body|
138
137
  status = body['taskState'] && body['taskState']['status']
139
- status == 'RUNNING' || status == 'PREPARED'
138
+ status == 'RUNNING' || status == 'PREPARED' || body['uri']
140
139
  end
141
140
  end
142
141