gooddata 2.1.19 → 2.3.0
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/LICENSE +4409 -16
- 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 +100 -68
- data/lib/gooddata/bricks/middleware/bulk_salesforce_middleware.rb +0 -37
- data/lib/gooddata/cloud_resources/redshift/drivers/log4j.properties +0 -15
data/lib/gooddata/lcm/lcm2.rb
CHANGED
@@ -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
|
-
|
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.
|
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
|
@@ -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(
|
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
|
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
|
-
|
47
|
-
|
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
|
-
|
53
|
-
|
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
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
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
|
|