gooddata 2.1.14-java → 2.2.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 +5 -5
- data/.gdc-ii-config.yaml +1 -1
- data/.github/workflows/build.yml +66 -0
- data/.github/workflows/pre-merge.yml +72 -0
- data/.sonar.settings +4 -0
- data/.travis.yml +78 -12
- data/CHANGELOG.md +62 -0
- data/Dockerfile +25 -14
- data/LICENSE +4418 -17
- data/LICENSE.rb +1 -1
- data/README.md +3 -3
- data/Rakefile +8 -1
- data/SDK_VERSION +1 -1
- data/VERSION +1 -1
- data/bin/test_projects_cleanup.rb +45 -3
- data/ci/mssql/pom.xml +62 -0
- data/ci/mysql/pom.xml +57 -0
- data/ci/postgresql/pom.xml +57 -0
- data/ci/redshift/pom.xml +1 -1
- data/dev-gooddata-sso.pub.encrypted +40 -40
- data/gdc_fossa_ruby_sdk.yaml +1 -0
- data/gooddata.gemspec +10 -6
- data/k8s/charts/lcm-bricks/Chart.yaml +1 -1
- data/k8s/charts/lcm-bricks/templates/prometheus/alertingRules.yaml +11 -1
- data/lcm.rake +2 -2
- data/lib/gooddata/bricks/middleware/aws_middleware.rb +35 -9
- data/lib/gooddata/cloud_resources/blobstorage/blobstorage_client.rb +98 -0
- data/lib/gooddata/cloud_resources/{cloud_resouce_factory.rb → cloud_resource_factory.rb} +8 -0
- data/lib/gooddata/cloud_resources/cloud_resources.rb +1 -1
- 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 +111 -0
- data/lib/gooddata/cloud_resources/postgresql/drivers/.gitkeepme +0 -0
- data/lib/gooddata/cloud_resources/postgresql/postgresql_client.rb +106 -0
- data/lib/gooddata/cloud_resources/snowflake/snowflake_client.rb +18 -1
- data/lib/gooddata/commands/scaffold.rb +9 -10
- data/lib/gooddata/core/nil_logger.rb +3 -1
- data/lib/gooddata/helpers/data_helper.rb +9 -5
- data/lib/gooddata/helpers/global_helpers.rb +6 -5
- data/lib/gooddata/lcm/actions/associate_clients.rb +8 -2
- data/lib/gooddata/lcm/actions/base_action.rb +0 -2
- data/lib/gooddata/lcm/actions/collect_meta.rb +3 -1
- data/lib/gooddata/lcm/actions/migrate_gdc_date_dimension.rb +3 -2
- data/lib/gooddata/lcm/actions/provision_clients.rb +31 -10
- data/lib/gooddata/lcm/actions/synchronize_clients.rb +56 -7
- data/lib/gooddata/lcm/actions/synchronize_dataset_mappings.rb +64 -0
- data/lib/gooddata/lcm/actions/synchronize_ldm.rb +19 -8
- data/lib/gooddata/lcm/actions/synchronize_user_filters.rb +16 -9
- data/lib/gooddata/lcm/actions/synchronize_users.rb +7 -6
- data/lib/gooddata/lcm/actions/update_metric_formats.rb +185 -0
- data/lib/gooddata/lcm/data/delete_from_lcm_release.sql.erb +5 -0
- data/lib/gooddata/lcm/helpers/release_table_helper.rb +42 -8
- data/lib/gooddata/lcm/lcm2.rb +5 -2
- data/lib/gooddata/lcm/types/base_type.rb +0 -2
- data/lib/gooddata/mixins/md_object_query.rb +9 -6
- data/lib/gooddata/models/blueprint/project_blueprint.rb +0 -2
- data/lib/gooddata/models/client.rb +14 -12
- data/lib/gooddata/models/data_source.rb +668 -0
- data/lib/gooddata/models/dataset_mapping.rb +36 -0
- data/lib/gooddata/models/domain.rb +3 -2
- data/lib/gooddata/models/metadata/analytical_dashboard.rb +49 -0
- data/lib/gooddata/models/metadata/analytical_visualization_object.rb +30 -0
- data/lib/gooddata/models/metadata/label.rb +26 -27
- data/lib/gooddata/models/metadata/visualization_object.rb +50 -0
- data/lib/gooddata/models/project.rb +66 -19
- data/lib/gooddata/models/schedule.rb +13 -1
- data/lib/gooddata/models/user_filters/user_filter_builder.rb +58 -54
- data/lib/gooddata/models/user_group.rb +0 -1
- data/lib/gooddata/rest/connection.rb +6 -4
- data/lib/gooddata/rest/phmap.rb +2 -1
- data/lib/gooddata.rb +2 -0
- data/rubydev_public.gpg.encrypted +51 -51
- data/rubydev_secret_keys.gpg.encrypted +109 -109
- metadata +51 -27
- data/DEPENDENCIES.md +0 -880
- data/lib/gooddata/bricks/middleware/bulk_salesforce_middleware.rb +0 -37
- data/lib/gooddata/helpers/data_source_helpers.rb +0 -47
@@ -5,7 +5,6 @@
|
|
5
5
|
# LICENSE file in the root directory of this source tree.
|
6
6
|
|
7
7
|
require 'cgi'
|
8
|
-
require 'active_support/core_ext/hash/compact'
|
9
8
|
|
10
9
|
require_relative 'profile'
|
11
10
|
require_relative '../extensions/enumerable'
|
@@ -457,10 +456,12 @@ Available values for setting language are: #{available_languages}."
|
|
457
456
|
alias_method :add_clients_settings, :update_clients_settings
|
458
457
|
|
459
458
|
def update_clients(data, options = {})
|
459
|
+
results = []
|
460
460
|
data.group_by(&:data_product_id).each do |data_product_id, client_update_data|
|
461
461
|
data_product = data_products(data_product_id)
|
462
|
-
data_product.update_clients(client_update_data, options)
|
462
|
+
results.concat data_product.update_clients(client_update_data, options)
|
463
463
|
end
|
464
|
+
results
|
464
465
|
end
|
465
466
|
|
466
467
|
# Update user in domain
|
@@ -0,0 +1,49 @@
|
|
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 'analytical_visualization_object'
|
9
|
+
|
10
|
+
module GoodData
|
11
|
+
class AnalyticalDashboard < GoodData::AnalyticalVisualizationObject
|
12
|
+
EMPTY_OBJECT = {
|
13
|
+
'analyticalDashboard' => {
|
14
|
+
'content' => {
|
15
|
+
'filterContext' => '',
|
16
|
+
'layout' => {},
|
17
|
+
'widgets' => []
|
18
|
+
},
|
19
|
+
'meta' => {
|
20
|
+
'deprecated' => '0',
|
21
|
+
'summary' => '',
|
22
|
+
'title' => ''
|
23
|
+
}
|
24
|
+
}
|
25
|
+
}
|
26
|
+
|
27
|
+
ASSIGNABLE_MEMBERS = %i[filterContext layout widgets deprecated summary title]
|
28
|
+
|
29
|
+
class << self
|
30
|
+
# Method intended to get all AnalyticalDashboard objects in a specified project
|
31
|
+
#
|
32
|
+
# @param options [Hash] the options hash
|
33
|
+
# @option options [Boolean] :full with true value will pull in full objects. Default is false value
|
34
|
+
# @return [Array<GoodData::AnalyticalDashboard>] Return AnalyticalDashboard list
|
35
|
+
def all(options = { :client => GoodData.connection, :project => GoodData.project })
|
36
|
+
query('analyticalDashboard', AnalyticalDashboard, options)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Create Analytical Dashboard in the specify project
|
40
|
+
#
|
41
|
+
# @param analytical_dashboard [Hash] the data of object will be created
|
42
|
+
# @param options [Hash] The project that the object will be created in
|
43
|
+
# @return GoodData::AnalyticalDashboard object
|
44
|
+
def create(analytical_dashboard = {}, options = { :client => GoodData.client, :project => GoodData.project })
|
45
|
+
GoodData::AnalyticalVisualizationObject.create(analytical_dashboard, AnalyticalDashboard, EMPTY_OBJECT, ASSIGNABLE_MEMBERS, options)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,30 @@
|
|
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 AnalyticalVisualizationObject < GoodData::MdObject
|
10
|
+
class << self
|
11
|
+
# Create a specify object in the specify project
|
12
|
+
#
|
13
|
+
# @param object_data [Hash] the data of object will be created
|
14
|
+
# @param klass [Class] A class used for instantiating the returned data
|
15
|
+
# @param empty_data_object [Hash] the empty data of object will be created
|
16
|
+
# @param assignable_properties [Hash] the properties allow updating
|
17
|
+
# @param options [Hash] The project that the object will be created in
|
18
|
+
# @return klass object
|
19
|
+
def create(object_data, klass, empty_data_object = {}, assignable_properties = [], options = { :client => GoodData.client, :project => GoodData.project })
|
20
|
+
client, project = GoodData.get_client_and_project(GoodData::Helpers.symbolize_keys(options))
|
21
|
+
|
22
|
+
res = client.create(klass, GoodData::Helpers.deep_dup(GoodData::Helpers.stringify_keys(empty_data_object)), :project => project)
|
23
|
+
object_data.each do |k, v|
|
24
|
+
res.send("#{k}=", v) if assignable_properties.include? k
|
25
|
+
end
|
26
|
+
res
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
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
|
|
@@ -0,0 +1,50 @@
|
|
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 'analytical_visualization_object'
|
9
|
+
|
10
|
+
module GoodData
|
11
|
+
class VisualizationObject < GoodData::AnalyticalVisualizationObject
|
12
|
+
EMPTY_OBJECT = {
|
13
|
+
'visualizationObject' => {
|
14
|
+
'content' => {
|
15
|
+
'buckets' => [],
|
16
|
+
'properties' => '',
|
17
|
+
'visualizationClass' => {}
|
18
|
+
},
|
19
|
+
'links' => {},
|
20
|
+
'meta' => {
|
21
|
+
'deprecated' => '0',
|
22
|
+
'summary' => '',
|
23
|
+
'title' => ''
|
24
|
+
}
|
25
|
+
}
|
26
|
+
}
|
27
|
+
|
28
|
+
ASSIGNABLE_MEMBERS = %i[buckets properties visualizationClass deprecated summary title]
|
29
|
+
|
30
|
+
class << self
|
31
|
+
# Method intended to get all VisualizationObject objects in a specified project
|
32
|
+
#
|
33
|
+
# @param options [Hash] the options hash
|
34
|
+
# @option options [Boolean] :full with true value to pull full objects
|
35
|
+
# @return [Array<GoodData::VisualizationObject>] Return VisualizationObject list
|
36
|
+
def all(options = { :client => GoodData.connection, :project => GoodData.project })
|
37
|
+
query('visualizationObject', VisualizationObject, options)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Create Visualization Object in the specify project
|
41
|
+
#
|
42
|
+
# @param visualization_object [Hash] the data of object will be created
|
43
|
+
# @param options [Hash] The project that the object will be created in
|
44
|
+
# @return GoodData::VisualizationObject object
|
45
|
+
def create(visualization_object = {}, options = { :client => GoodData.client, :project => GoodData.project })
|
46
|
+
GoodData::AnalyticalVisualizationObject.create(visualization_object, VisualizationObject, EMPTY_OBJECT, ASSIGNABLE_MEMBERS, options)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# encoding: UTF-8
|
2
2
|
#
|
3
|
-
# Copyright (c) 2010-
|
3
|
+
# Copyright (c) 2010-2021 GoodData Corporation. All rights reserved.
|
4
4
|
# This source code is licensed under the BSD-style license found in the
|
5
5
|
# LICENSE file in the root directory of this source tree.
|
6
6
|
|
@@ -13,7 +13,6 @@ require 'zip'
|
|
13
13
|
require 'net/smtp'
|
14
14
|
|
15
15
|
require 'active_support/core_ext/hash/except'
|
16
|
-
require 'active_support/core_ext/hash/compact'
|
17
16
|
require 'active_support/core_ext/hash/slice'
|
18
17
|
|
19
18
|
require_relative '../exceptions/no_project_error'
|
@@ -31,6 +30,7 @@ require_relative 'process'
|
|
31
30
|
require_relative 'project_log_formatter'
|
32
31
|
require_relative 'project_role'
|
33
32
|
require_relative 'blueprint/blueprint'
|
33
|
+
require_relative 'dataset_mapping'
|
34
34
|
|
35
35
|
require_relative 'metadata/scheduled_mail'
|
36
36
|
require_relative 'metadata/scheduled_mail/dashboard_attachment'
|
@@ -38,7 +38,8 @@ require_relative 'metadata/scheduled_mail/report_attachment'
|
|
38
38
|
|
39
39
|
module GoodData
|
40
40
|
class Project < Rest::Resource
|
41
|
-
|
41
|
+
USER_ACCOUNT_PATH = '/gdc/account/profile/'
|
42
|
+
USERSPROJECTS_PATH = USER_ACCOUNT_PATH + '%s/projects'
|
42
43
|
PROJECTS_PATH = '/gdc/projects'
|
43
44
|
PROJECT_PATH = '/gdc/projects/%s'
|
44
45
|
SLIS_PATH = '/ldm/singleloadinterface'
|
@@ -255,6 +256,22 @@ module GoodData
|
|
255
256
|
transfer_schedules(from_project, to_project)
|
256
257
|
end
|
257
258
|
|
259
|
+
def get_dataset_mapping(from_project)
|
260
|
+
GoodData::DatasetMapping.get(:client => from_project.client, :project => from_project)
|
261
|
+
end
|
262
|
+
|
263
|
+
def update_dataset_mapping(model_mapping_json, to_project)
|
264
|
+
dataset_mapping = GoodData::DatasetMapping.new(model_mapping_json)
|
265
|
+
res = dataset_mapping.save(:client => to_project.client, :project => to_project)
|
266
|
+
status = res&.dig('datasetMappings', 'items').nil? ? "Failed" : "OK"
|
267
|
+
count = "OK".eql?(status) ? res['datasetMappings']['items'].length : 0
|
268
|
+
{
|
269
|
+
to: to_project.pid,
|
270
|
+
count: count,
|
271
|
+
status: status
|
272
|
+
}
|
273
|
+
end
|
274
|
+
|
258
275
|
# @param from_project The source project
|
259
276
|
# @param to_project The target project
|
260
277
|
# @param options Optional parameters
|
@@ -336,26 +353,22 @@ module GoodData
|
|
336
353
|
|
337
354
|
def get_data_source_alias(data_source_id, client, aliases)
|
338
355
|
unless aliases[data_source_id]
|
339
|
-
data_source = GoodData::
|
340
|
-
if data_source&.
|
356
|
+
data_source = GoodData::DataSource.from_id(data_source_id, client: client)
|
357
|
+
if data_source&.alias
|
341
358
|
aliases[data_source_id] = {
|
342
|
-
:type =>
|
343
|
-
:alias => data_source
|
359
|
+
:type => data_source.type,
|
360
|
+
:alias => data_source.alias
|
344
361
|
}
|
345
362
|
end
|
346
363
|
end
|
347
364
|
aliases[data_source_id]
|
348
365
|
end
|
349
366
|
|
350
|
-
def get_data_source_type(data_source_data)
|
351
|
-
data_source_data&.dig('dataSource', 'connectionInfo') ? data_source_data['dataSource']['connectionInfo'].first[0].upcase : ""
|
352
|
-
end
|
353
|
-
|
354
367
|
def replace_process_data_source_ids(process_data, client, aliases)
|
355
368
|
component = process_data.dig(:process, :component)
|
356
369
|
if component&.dig(:configLocation, :dataSourceConfig)
|
357
370
|
the_alias = aliases[component[:configLocation][:dataSourceConfig][:id]]
|
358
|
-
process_data[:process][:component][:configLocation][:dataSourceConfig][:id] =
|
371
|
+
process_data[:process][:component][:configLocation][:dataSourceConfig][:id] = verify_data_source_alias(the_alias, client)
|
359
372
|
end
|
360
373
|
process_data[:process][:dataSources] = replace_data_source_ids(process_data[:process][:dataSources], client, aliases)
|
361
374
|
process_data
|
@@ -365,13 +378,35 @@ module GoodData
|
|
365
378
|
array_data_sources = []
|
366
379
|
if data_sources && !data_sources.empty?
|
367
380
|
data_sources.map do |data_source|
|
368
|
-
new_id =
|
381
|
+
new_id = verify_data_source_alias(aliases[data_source[:id]], client)
|
369
382
|
array_data_sources.push(:id => new_id)
|
370
383
|
end
|
371
384
|
end
|
372
385
|
array_data_sources
|
373
386
|
end
|
374
387
|
|
388
|
+
# Verify whether the data source exists in the domain using its alias
|
389
|
+
#
|
390
|
+
# @param [String] ds_alias The data source's alias
|
391
|
+
# @param [Object] client The Rest Client object
|
392
|
+
# @return [String] Id of the data source or failed with the reason
|
393
|
+
def verify_data_source_alias(ds_alias, client)
|
394
|
+
domain = client.connection.server.url
|
395
|
+
fail "The data source alias is empty, check your data source configuration." unless ds_alias
|
396
|
+
|
397
|
+
uri = "/gdc/dataload/dataSources/internal/availableAlias?alias=#{ds_alias[:alias]}"
|
398
|
+
res = client.get(uri)
|
399
|
+
fail "Unable to get information about the Data Source '#{ds_alias[:alias]}' in the domain '#{domain}'" unless res
|
400
|
+
fail "Unable to find the #{ds_alias[:type]} Data Source '#{ds_alias[:alias]}' in the domain '#{domain}'" if res['availableAlias']['available']
|
401
|
+
|
402
|
+
ds_type = res['availableAlias']['existingDataSource']['type']
|
403
|
+
if ds_type && ds_type != ds_alias[:type]
|
404
|
+
fail "Wrong Data Source type - the '#{ds_type}' type is expected but the Data Source '#{ds_alias[:alias]}' in the domain '#{domain}' has the '#{ds_alias[:type]}' type"
|
405
|
+
else
|
406
|
+
res['availableAlias']['existingDataSource']['id']
|
407
|
+
end
|
408
|
+
end
|
409
|
+
|
375
410
|
def transfer_user_groups(from_project, to_project)
|
376
411
|
from_project.user_groups.map do |ug|
|
377
412
|
# migrate groups
|
@@ -438,7 +473,9 @@ module GoodData
|
|
438
473
|
local_stuff = local_schedules.map do |s|
|
439
474
|
v = s.to_hash
|
440
475
|
after_schedule = local_schedules.find { |s2| s.trigger_id == s2.obj_id }
|
441
|
-
|
476
|
+
after_process_schedule = from_project_processes.find { |p| after_schedule && p.obj_id == after_schedule.process_id }
|
477
|
+
v[:after] = s.trigger_id && after_process_schedule && after_schedule && after_schedule.name
|
478
|
+
v[:trigger_execution_status] = s.trigger_execution_status
|
442
479
|
v[:remote_schedule] = s
|
443
480
|
v[:params] = v[:params].except("EXECUTABLE", "PROCESS_ID")
|
444
481
|
v.compact
|
@@ -507,6 +544,7 @@ module GoodData
|
|
507
544
|
schedule.params = (schedule_spec[:params] || {})
|
508
545
|
schedule.cron = schedule_spec[:cron] if schedule_spec[:cron]
|
509
546
|
schedule.after = schedule_cache[schedule_spec[:after]] if schedule_spec[:after]
|
547
|
+
schedule.trigger_execution_status = schedule_cache[schedule_spec[:trigger_execution_status]] if schedule_spec[:after]
|
510
548
|
schedule.hidden_params = schedule_spec[:hidden_params] || {}
|
511
549
|
if process_spec.type != :dataload
|
512
550
|
schedule.executable = schedule_spec[:executable] || (process_spec.type == :ruby ? 'main.rb' : 'main.grf')
|
@@ -567,7 +605,8 @@ module GoodData
|
|
567
605
|
hidden_params: schedule_spec[:hidden_params],
|
568
606
|
name: schedule_spec[:name],
|
569
607
|
reschedule: schedule_spec[:reschedule],
|
570
|
-
state: schedule_spec[:state]
|
608
|
+
state: schedule_spec[:state],
|
609
|
+
trigger_execution_status: schedule_spec[:trigger_execution_status]
|
571
610
|
}
|
572
611
|
end
|
573
612
|
end
|
@@ -1716,7 +1755,7 @@ module GoodData
|
|
1716
1755
|
end
|
1717
1756
|
end
|
1718
1757
|
diff_results = diff_results.map do |u|
|
1719
|
-
u[:login_uri] =
|
1758
|
+
u[:login_uri] = USER_ACCOUNT_PATH + u[:login]
|
1720
1759
|
u
|
1721
1760
|
end
|
1722
1761
|
return diff_results if options[:dry_run]
|
@@ -1952,17 +1991,17 @@ module GoodData
|
|
1952
1991
|
|
1953
1992
|
def resolve_roles(login, desired_roles, options = {})
|
1954
1993
|
user = if login.is_a?(String) && login.include?('@')
|
1955
|
-
|
1994
|
+
USER_ACCOUNT_PATH + login
|
1956
1995
|
elsif login.is_a?(String)
|
1957
1996
|
login
|
1958
1997
|
elsif login.is_a?(Hash) && login[:login]
|
1959
|
-
|
1998
|
+
USER_ACCOUNT_PATH + login[:login]
|
1960
1999
|
elsif login.is_a?(Hash) && login[:uri]
|
1961
2000
|
login[:uri]
|
1962
2001
|
elsif login.respond_to?(:uri) && login.uri
|
1963
2002
|
login.uri
|
1964
2003
|
elsif login.respond_to?(:login) && login.login
|
1965
|
-
|
2004
|
+
USER_ACCOUNT_PATH + login.login
|
1966
2005
|
else
|
1967
2006
|
fail "Unsupported user specification #{login}"
|
1968
2007
|
end
|
@@ -2000,6 +2039,14 @@ module GoodData
|
|
2000
2039
|
GoodData::Project.transfer_etl(client, self, target)
|
2001
2040
|
end
|
2002
2041
|
|
2042
|
+
def dataset_mapping
|
2043
|
+
GoodData::Project.get_dataset_mapping(self)
|
2044
|
+
end
|
2045
|
+
|
2046
|
+
def update_dataset_mapping(model_mapping_json)
|
2047
|
+
GoodData::Project.update_dataset_mapping(model_mapping_json, self)
|
2048
|
+
end
|
2049
|
+
|
2003
2050
|
def transfer_processes(target)
|
2004
2051
|
GoodData::Project.transfer_processes(self, target)
|
2005
2052
|
end
|
@@ -101,6 +101,7 @@ module GoodData
|
|
101
101
|
|
102
102
|
schedule.name = options[:name]
|
103
103
|
schedule.set_trigger(trigger)
|
104
|
+
schedule.trigger_execution_status = options[:trigger_execution_status]
|
104
105
|
schedule.params = default_opts[:params].merge(options[:params] || {})
|
105
106
|
schedule.hidden_params = options[:hidden_params] || {}
|
106
107
|
schedule.timezone = options[:timezone] || default_opts[:timezone]
|
@@ -468,6 +469,7 @@ module GoodData
|
|
468
469
|
hidden_params: hidden_params,
|
469
470
|
cron: cron,
|
470
471
|
trigger_id: trigger_id,
|
472
|
+
trigger_execution_status: trigger_execution_status,
|
471
473
|
timezone: timezone,
|
472
474
|
uri: uri,
|
473
475
|
reschedule: reschedule,
|
@@ -486,6 +488,16 @@ module GoodData
|
|
486
488
|
self
|
487
489
|
end
|
488
490
|
|
491
|
+
def trigger_execution_status
|
492
|
+
json['schedule']['triggerExecutionStatus']
|
493
|
+
end
|
494
|
+
|
495
|
+
def trigger_execution_status=(trigger_execution_status)
|
496
|
+
json['schedule']['triggerExecutionStatus'] = trigger_execution_status
|
497
|
+
@dirty = true
|
498
|
+
self # rubocop:disable Lint/Void
|
499
|
+
end
|
500
|
+
|
489
501
|
def name
|
490
502
|
json['schedule']['name']
|
491
503
|
end
|
@@ -530,7 +542,7 @@ module GoodData
|
|
530
542
|
'hiddenParams' => GoodData::Helpers.encode_hidden_params(hidden_params)
|
531
543
|
}
|
532
544
|
}
|
533
|
-
|
545
|
+
res['schedule']['triggerExecutionStatus'] = trigger_execution_status if trigger_execution_status
|
534
546
|
res['schedule']['reschedule'] = reschedule if reschedule
|
535
547
|
|
536
548
|
res
|
@@ -7,7 +7,6 @@
|
|
7
7
|
require_relative '../project_log_formatter'
|
8
8
|
|
9
9
|
require 'active_support/core_ext/hash/indifferent_access'
|
10
|
-
require 'active_support/core_ext/hash/compact'
|
11
10
|
|
12
11
|
require 'gooddata/extensions/true'
|
13
12
|
require 'gooddata/extensions/false'
|
@@ -204,7 +203,7 @@ module GoodData
|
|
204
203
|
# so it precaches the values and still be able to function for larger ones even
|
205
204
|
# though that would mean tons of requests
|
206
205
|
def self.get_small_labels(labels_cache)
|
207
|
-
labels_cache.values.select { |label| label
|
206
|
+
labels_cache.values.select { |label| label &.values_count &. < 100_000 }
|
208
207
|
end
|
209
208
|
|
210
209
|
# Creates a MAQL expression(s) based on the filter defintion.
|
@@ -422,68 +421,73 @@ module GoodData
|
|
422
421
|
results: create_results + delete_results }
|
423
422
|
end
|
424
423
|
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
'userFilters'
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
424
|
+
if to_create.empty?
|
425
|
+
create_results = []
|
426
|
+
else
|
427
|
+
create_results = to_create.each_slice(100).flat_map do |batch|
|
428
|
+
batch.pmapcat do |related_uri, group|
|
429
|
+
group.each(&:save)
|
430
|
+
res = client.get("/gdc/md/#{project.pid}/userfilters?users=#{related_uri}")
|
431
|
+
items = res['userFilters']['items'].empty? ? [] : res['userFilters']['items'].first['userFilters']
|
432
|
+
|
433
|
+
payload = {
|
434
|
+
'userFilters' => {
|
435
|
+
'items' => [{
|
436
|
+
'user' => related_uri,
|
437
|
+
'userFilters' => items.concat(group.map(&:uri))
|
438
|
+
}]
|
439
|
+
}
|
437
440
|
}
|
438
|
-
|
439
|
-
res = client.post("/gdc/md/#{project.pid}/userfilters", payload)
|
441
|
+
res = client.post("/gdc/md/#{project.pid}/userfilters", payload)
|
440
442
|
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
443
|
+
# turn the errors from hashes into array of hashes
|
444
|
+
update_result = res['userFiltersUpdateResult'].flat_map do |k, v|
|
445
|
+
v.map { |r| { status: k.to_sym, user: r, type: :create } }
|
446
|
+
end
|
445
447
|
|
446
|
-
|
447
|
-
|
448
|
+
update_result.map do |result|
|
449
|
+
result[:status] == :failed ? result.merge(GoodData::Helpers.symbolize_keys(result[:user])) : result
|
450
|
+
end
|
448
451
|
end
|
449
452
|
end
|
453
|
+
project_log_formatter.log_user_filter_results(create_results, to_create)
|
454
|
+
create_errors = create_results.select { |r| r[:status] == :failed }
|
455
|
+
fail "Creating MUFs resulted in errors: #{create_errors}" if create_errors.any?
|
450
456
|
end
|
451
457
|
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
res = client.post("/gdc/md/#{project.pid}/userfilters", payload)
|
474
|
-
results.concat(res['userFiltersUpdateResult']
|
458
|
+
if to_delete.empty?
|
459
|
+
delete_results = []
|
460
|
+
elsif !options[:do_not_touch_filters_that_are_not_mentioned]
|
461
|
+
delete_results = to_delete.each_slice(100).flat_map do |batch|
|
462
|
+
batch.flat_map do |related_uri, group|
|
463
|
+
results = []
|
464
|
+
if related_uri
|
465
|
+
res = client.get("/gdc/md/#{project.pid}/userfilters?users=#{related_uri}")
|
466
|
+
items = res['userFilters']['items'].empty? ? [] : res['userFilters']['items'].first['userFilters']
|
467
|
+
payload = {
|
468
|
+
'userFilters' => {
|
469
|
+
'items' => [
|
470
|
+
{
|
471
|
+
'user' => related_uri,
|
472
|
+
'userFilters' => items - group.map(&:uri)
|
473
|
+
}
|
474
|
+
]
|
475
|
+
}
|
476
|
+
}
|
477
|
+
res = client.post("/gdc/md/#{project.pid}/userfilters", payload)
|
478
|
+
results.concat(res['userFiltersUpdateResult']
|
475
479
|
.flat_map { |k, v| v.map { |r| { status: k.to_sym, user: r, type: :delete } } }
|
476
480
|
.map { |result| result[:status] == :failed ? result.merge(GoodData::Helpers.symbolize_keys(result[:user])) : result })
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
end
|
482
|
-
end
|
481
|
+
end
|
482
|
+
group.peach(&:delete)
|
483
|
+
results
|
484
|
+
end
|
483
485
|
|
484
|
-
|
485
|
-
|
486
|
-
|
486
|
+
project_log_formatter.log_user_filter_results(delete_results, to_delete)
|
487
|
+
delete_errors = delete_results.select { |r| r[:status] == :failed } if delete_results
|
488
|
+
fail "Deleting MUFs resulted in errors: #{delete_errors}" if delete_errors&.any?
|
489
|
+
end
|
490
|
+
end
|
487
491
|
|
488
492
|
{ created: to_create, deleted: to_delete, results: create_results + (delete_results || []) }
|
489
493
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# encoding: UTF-8
|
2
2
|
#
|
3
|
-
# Copyright (c) 2010-
|
3
|
+
# Copyright (c) 2010-2021 GoodData Corporation. All rights reserved.
|
4
4
|
# This source code is licensed under the BSD-style license found in the
|
5
5
|
# LICENSE file in the root directory of this source tree.
|
6
6
|
|
@@ -26,6 +26,8 @@ module GoodData
|
|
26
26
|
class Connection
|
27
27
|
include MonitorMixin
|
28
28
|
|
29
|
+
HTTPS_PROTOCOL = 'https://'
|
30
|
+
HTTP_PROTOCOL = 'http://'
|
29
31
|
DEFAULT_URL = 'https://secure.gooddata.com'
|
30
32
|
LOGIN_PATH = '/gdc/account/login'
|
31
33
|
TOKEN_PATH = '/gdc/account/token'
|
@@ -712,12 +714,12 @@ ERR
|
|
712
714
|
|
713
715
|
def fix_server_url(server)
|
714
716
|
server = server.chomp('/')
|
715
|
-
if server.start_with?
|
716
|
-
server = server.sub
|
717
|
+
if server.start_with? HTTP_PROTOCOL
|
718
|
+
server = server.sub HTTP_PROTOCOL, HTTPS_PROTOCOL
|
717
719
|
GoodData.logger.warn 'You specified the HTTP protocol in your server string. It has been autofixed to HTTPS.'
|
718
720
|
end
|
719
721
|
|
720
|
-
server =
|
722
|
+
server = HTTPS_PROTOCOL + server unless server.start_with? HTTPS_PROTOCOL
|
721
723
|
server
|
722
724
|
end
|
723
725
|
end
|