gooddata 0.6.53 → 0.6.54
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.flayignore +6 -0
- data/.gitignore +1 -0
- data/.pronto.yml +3 -0
- data/.rspec +2 -0
- data/.rubocop.yml +4 -1
- data/CHANGELOG.md +18 -0
- data/CONTRIBUTING.md +14 -1
- data/DEPENDENCIES.md +324 -253
- data/Dockerfile.jruby +5 -7
- data/Dockerfile.ruby +8 -8
- data/Rakefile +24 -0
- data/ci.rake +47 -0
- data/docker-compose.yml +34 -0
- data/gooddata.gemspec +8 -2
- data/lib/gooddata/bricks/middleware/restforce_middleware.rb +0 -3
- data/lib/gooddata/helpers/data_helper.rb +10 -7
- data/lib/gooddata/helpers/global_helpers_params.rb +8 -3
- data/lib/gooddata/lcm/actions/apply_custom_maql.rb +2 -1
- data/lib/gooddata/lcm/actions/associate_clients.rb +10 -1
- data/lib/gooddata/lcm/actions/collect_client_projects.rb +78 -0
- data/lib/gooddata/lcm/actions/collect_clients.rb +20 -6
- data/lib/gooddata/lcm/actions/collect_data_product.rb +62 -0
- data/lib/gooddata/lcm/actions/collect_dynamic_schedule_params.rb +62 -0
- data/lib/gooddata/lcm/actions/{collect_attrs.rb → collect_ldm_objects.rb} +3 -3
- data/lib/gooddata/lcm/actions/collect_meta.rb +6 -3
- data/lib/gooddata/lcm/actions/collect_segment_clients.rb +2 -1
- data/lib/gooddata/lcm/actions/collect_segments.rb +6 -7
- data/lib/gooddata/lcm/actions/collect_tagged_objects.rb +7 -4
- data/lib/gooddata/lcm/actions/create_segment_masters.rb +7 -3
- data/lib/gooddata/lcm/actions/ensure_data_product.rb +53 -0
- data/lib/gooddata/lcm/actions/ensure_technical_users_domain.rb +6 -2
- data/lib/gooddata/lcm/actions/ensure_technical_users_project.rb +30 -18
- data/lib/gooddata/lcm/actions/execute_schedules.rb +128 -0
- data/lib/gooddata/lcm/actions/provision_clients.rb +32 -21
- data/lib/gooddata/lcm/actions/purge_clients.rb +25 -39
- data/lib/gooddata/lcm/actions/rename_existing_client_projects.rb +70 -0
- data/lib/gooddata/lcm/actions/segments_filter.rb +6 -0
- data/lib/gooddata/lcm/actions/synchronize_cas.rb +11 -0
- data/lib/gooddata/lcm/actions/synchronize_clients.rb +2 -1
- data/lib/gooddata/lcm/actions/synchronize_etls_in_segment.rb +34 -15
- data/lib/gooddata/lcm/actions/synchronize_ldm.rb +10 -1
- data/lib/gooddata/lcm/actions/synchronize_new_segments.rb +2 -1
- data/lib/gooddata/lcm/actions/synchronize_processes.rb +4 -7
- data/lib/gooddata/lcm/actions/synchronize_tag_objects.rb +8 -5
- data/lib/gooddata/lcm/actions/synchronize_user_filters.rb +224 -0
- data/lib/gooddata/lcm/actions/synchronize_user_groups.rb +53 -0
- data/lib/gooddata/lcm/actions/synchronize_users.rb +324 -0
- data/lib/gooddata/lcm/dsl/type_dsl.rb +1 -0
- data/lib/gooddata/lcm/helpers/check_helper.rb +4 -0
- data/lib/gooddata/lcm/helpers/tags_helper.rb +4 -3
- data/lib/gooddata/lcm/lcm2.rb +33 -1
- data/lib/gooddata/lcm/types/complex/segment.rb +3 -0
- data/lib/gooddata/lcm/types/complex/update_preference.rb +8 -2
- data/lib/gooddata/lcm/types/special/array.rb +1 -3
- data/lib/gooddata/lcm/types/special/enum.rb +1 -3
- data/lib/gooddata/mixins/md_id_to_uri.rb +0 -1
- data/lib/gooddata/mixins/md_json.rb +2 -2
- data/lib/gooddata/models/blueprint/project_blueprint.rb +15 -0
- data/lib/gooddata/models/blueprint/to_wire.rb +1 -0
- data/lib/gooddata/models/client.rb +21 -9
- data/lib/gooddata/models/data_product.rb +149 -0
- data/lib/gooddata/models/domain.rb +26 -72
- data/lib/gooddata/models/from_wire.rb +2 -0
- data/lib/gooddata/models/metadata/report.rb +9 -3
- data/lib/gooddata/models/metadata/report_definition.rb +2 -2
- data/lib/gooddata/models/model.rb +1 -1
- data/lib/gooddata/models/process.rb +4 -0
- data/lib/gooddata/models/project.rb +58 -35
- data/lib/gooddata/models/project_creator.rb +13 -0
- data/lib/gooddata/models/segment.rb +63 -16
- data/lib/gooddata/models/style_setting.rb +2 -15
- data/lib/gooddata/models/user_group.rb +2 -0
- data/lib/gooddata/rest/connection.rb +32 -9
- data/lib/gooddata/rest/object_factory.rb +0 -25
- data/lib/gooddata/version.rb +1 -1
- data/spec/data/blueprints/invalid_blueprint.json +2 -2
- data/spec/data/blueprints/test_project_model_spec.json +1 -1
- data/spec/data/dynamic_schedule_params_table.csv +7 -0
- data/spec/data/workspace_table.csv +3 -3
- data/spec/environment/staging.rb +3 -3
- data/spec/integration/ads_output_stage_spec.rb +0 -10
- data/spec/integration/clients_spec.rb +1 -1
- data/spec/{unit → integration}/commands/command_projects_spec.rb +0 -0
- data/spec/{unit → integration}/core/connection_spec.rb +0 -0
- data/spec/{unit → integration}/core/logging_spec.rb +0 -0
- data/spec/{unit → integration}/core/project_spec.rb +0 -0
- data/spec/integration/date_dim_switch_spec.rb +13 -0
- data/spec/integration/full_process_schedule_spec.rb +2 -2
- data/spec/integration/helpers_spec.rb +16 -0
- data/spec/integration/lcm_spec.rb +12 -2
- data/spec/integration/mixins/id_to_uri_spec.rb +44 -0
- data/spec/integration/models/data_product_spec.rb +71 -0
- data/spec/{unit → integration}/models/domain_spec.rb +2 -2
- data/spec/{unit → integration}/models/invitation_spec.rb +0 -0
- data/spec/{unit → integration}/models/membership_spec.rb +0 -0
- data/spec/{unit → integration}/models/params_spec.rb +0 -0
- data/spec/{unit → integration}/models/profile_spec.rb +0 -0
- data/spec/{unit → integration}/models/project_role_spec.rb +0 -0
- data/spec/integration/models/project_spec.rb +225 -0
- data/spec/{unit → integration}/models/schedule_spec.rb +0 -0
- data/spec/{unit → integration}/models/unit_project_spec.rb +0 -0
- data/spec/integration/project_spec.rb +40 -5
- data/spec/integration/segments_spec.rb +27 -26
- data/spec/integration/user_filters_spec.rb +1 -1
- data/spec/spec_helper.rb +15 -19
- data/spec/unit/actions/associate_clients_spec.rb +47 -0
- data/spec/unit/actions/collect_client_projects_spec.rb +47 -0
- data/spec/unit/actions/collect_clients_spec.rb +27 -0
- data/spec/unit/actions/collect_data_product_spec.rb +64 -0
- data/spec/unit/actions/collect_dynamic_schedule_params_spec.rb +56 -0
- data/spec/unit/actions/collect_meta_spec.rb +4 -4
- data/spec/unit/actions/collect_segment_clients_spec.rb +44 -3
- data/spec/unit/actions/collect_tagged_objects_spec.rb +20 -4
- data/spec/unit/actions/create_segment_masters_spec.rb +64 -0
- data/spec/unit/actions/ensure_data_product_spec.rb +38 -0
- data/spec/unit/actions/ensure_technical_users_domain_spec.rb +51 -0
- data/spec/unit/actions/ensure_technical_users_project_spec.rb +72 -0
- data/spec/unit/actions/execute_schedules_spec.rb +94 -0
- data/spec/unit/actions/provision_clients_spec.rb +45 -0
- data/spec/unit/actions/purge_clients_spec.rb +47 -0
- data/spec/unit/actions/rename_existing_client_projects_spec.rb +54 -0
- data/spec/unit/actions/segments_filter_spec.rb +46 -0
- data/spec/unit/actions/shared_examples_for_user_actions.rb +10 -0
- data/spec/unit/actions/synchronize_cas_spec.rb +58 -0
- data/spec/unit/actions/synchronize_etls_in_segment_spec.rb +174 -13
- data/spec/unit/actions/synchronize_ldm_spec.rb +57 -0
- data/spec/unit/actions/synchronize_user_filters_spec.rb +142 -0
- data/spec/unit/actions/synchronize_user_groups_spec.rb +49 -0
- data/spec/unit/actions/synchronize_users_spec.rb +76 -0
- data/spec/unit/helpers/data_helper_spec.rb +17 -0
- data/spec/unit/helpers/global_helpers_spec.rb +16 -0
- data/spec/unit/helpers_spec.rb +0 -6
- data/spec/unit/models/blueprint/project_blueprint_spec.rb +21 -4
- data/spec/unit/models/project_creator_spec.rb +16 -0
- data/spec/unit/models/project_spec.rb +66 -197
- metadata +202 -100
- data/PULL_REQUEST_TEMPLATE.md +0 -5
- data/lib/gooddata/bricks/middleware/params_inspect_middleware.rb +0 -21
@@ -14,8 +14,6 @@ module GoodData
|
|
14
14
|
class Domain < Rest::Resource
|
15
15
|
attr_reader :name
|
16
16
|
|
17
|
-
ProvisioningResult = Struct.new('ProvisioningResult', :id, :status, :project_uri, :error)
|
18
|
-
|
19
17
|
USER_LANGUAGES = {
|
20
18
|
'en-US' => 'English',
|
21
19
|
'nl-NL' => 'Dutch',
|
@@ -95,6 +93,10 @@ module GoodData
|
|
95
93
|
tmp = user_data[:timezone]
|
96
94
|
data[:timezone] = tmp if tmp && !tmp.empty?
|
97
95
|
|
96
|
+
# Optional ip whitelist
|
97
|
+
tmp = user_data[:ip_whitelist]
|
98
|
+
data[:ipWhitelist] = tmp if tmp
|
99
|
+
|
98
100
|
c = client(opts)
|
99
101
|
|
100
102
|
# TODO: It will be nice if the API will return us user just newly created
|
@@ -323,19 +325,11 @@ Available values for setting language are: #{available_languages}."
|
|
323
325
|
# if it exists.
|
324
326
|
#
|
325
327
|
# @param id [String] Id of client that you are looking for
|
328
|
+
# @param data_product [DataProduct] data product object in which the clients are located
|
326
329
|
# @return [Object] Raw response
|
327
330
|
#
|
328
|
-
def clients(id = :all)
|
329
|
-
|
330
|
-
res = client.get("/gdc/domains/#{name}/clients")
|
331
|
-
res_clients = (res['clients'] && res['clients']['items']) || []
|
332
|
-
res_clients.map { |res_client| client.create(GoodData::Client, res_client) }
|
333
|
-
else
|
334
|
-
res = client.get("/gdc/domains/#{name}/clients/#{id}")
|
335
|
-
error = GoodData::Helpers.interpolate_error_message(res)
|
336
|
-
raise error if error
|
337
|
-
client.create(GoodData::Client, res)
|
338
|
-
end
|
331
|
+
def clients(id = :all, data_product = nil)
|
332
|
+
GoodData::Client[id, data_product: data_product, domain: self]
|
339
333
|
end
|
340
334
|
|
341
335
|
alias_method :create_user, :add_user
|
@@ -344,8 +338,17 @@ Available values for setting language are: #{available_languages}."
|
|
344
338
|
GoodData::Domain.create_users(list, name, { client: client }.merge(options))
|
345
339
|
end
|
346
340
|
|
347
|
-
def
|
348
|
-
GoodData::
|
341
|
+
def data_products(id = :all)
|
342
|
+
GoodData::DataProduct[id, domain: self]
|
343
|
+
end
|
344
|
+
|
345
|
+
def create_data_product(data)
|
346
|
+
data_product = GoodData::DataProduct.create(data, domain: self, client: client)
|
347
|
+
data_product.save
|
348
|
+
end
|
349
|
+
|
350
|
+
def segments(id = :all, data_product = nil)
|
351
|
+
GoodData::Segment[id, data_product: data_product, domain: self]
|
349
352
|
end
|
350
353
|
|
351
354
|
# Creates new segment in current domain from parameters passed
|
@@ -429,29 +432,10 @@ Available values for setting language are: #{available_languages}."
|
|
429
432
|
#
|
430
433
|
# @return [Enumerator] Returns Enumerator of results
|
431
434
|
def provision_client_projects(segments = nil)
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
}
|
437
|
-
}
|
438
|
-
end
|
439
|
-
|
440
|
-
res = client.post(segments_uri + '/provisionClientProjects', body)
|
441
|
-
res = client.poll_on_code(res['asyncTask']['links']['poll'])
|
442
|
-
failed_count = GoodData::Helpers.get_path(res, %w(clientProjectProvisioningResult failed count), 0)
|
443
|
-
created_count = GoodData::Helpers.get_path(res, %w(clientProjectProvisioningResult created count), 0)
|
444
|
-
return Enumerator.new([]) if failed_count + created_count == 0 # rubocop:disable Style/NumericPredicate
|
445
|
-
Enumerator.new do |y|
|
446
|
-
uri = GoodData::Helpers.get_path(res, %w(clientProjectProvisioningResult links details))
|
447
|
-
loop do
|
448
|
-
result = client.get(uri)
|
449
|
-
(GoodData::Helpers.get_path(result, %w(clientProjectProvisioningResultDetails items)) || []).each do |item|
|
450
|
-
y << ProvisioningResult.new(item['id'], item['status'], item['project'], item['error'])
|
451
|
-
end
|
452
|
-
uri = GoodData::Helpers.get_path(res, %w(clientProjectProvisioningResultDetails paging next))
|
453
|
-
break if uri.nil?
|
454
|
-
end
|
435
|
+
segments = segments.is_a?(Array) ? segments : [segments]
|
436
|
+
segments.each do |segment_id|
|
437
|
+
segment = segments(segment_id)
|
438
|
+
segment.provision_client_projects
|
455
439
|
end
|
456
440
|
end
|
457
441
|
|
@@ -460,7 +444,7 @@ Available values for setting language are: #{available_languages}."
|
|
460
444
|
client_id = datum[:id]
|
461
445
|
settings = datum[:settings]
|
462
446
|
settings.each do |setting|
|
463
|
-
GoodData::Client.update_setting(setting[:name], setting[:value], domain: self, client_id: client_id)
|
447
|
+
GoodData::Client.update_setting(setting[:name], setting[:value], domain: self, client_id: client_id, data_product_id: datum[:data_product_id])
|
464
448
|
end
|
465
449
|
end
|
466
450
|
nil
|
@@ -468,39 +452,9 @@ Available values for setting language are: #{available_languages}."
|
|
468
452
|
alias_method :add_clients_settings, :update_clients_settings
|
469
453
|
|
470
454
|
def update_clients(data, options = {})
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
delete_projects = options[:delete_projects] == false ? false : true
|
476
|
-
payload = data.map do |datum|
|
477
|
-
{
|
478
|
-
:client => {
|
479
|
-
:id => datum[:id],
|
480
|
-
:segment => segments_uri + '/segments/' + datum[:segment]
|
481
|
-
}
|
482
|
-
}.tap do |h|
|
483
|
-
h[:client][:project] = datum[:project] if datum.key?(:project)
|
484
|
-
end
|
485
|
-
end
|
486
|
-
if options[:delete_extra] == true
|
487
|
-
res = client.post(segments_uri + '/updateClients?deleteExtra=true', updateClients: { items: payload })
|
488
|
-
elsif options[:delete_extra_in_segments]
|
489
|
-
segments_to_delete_in = options[:delete_extra_in_segments]
|
490
|
-
.map { |segment| CGI.escape(segment) }
|
491
|
-
.join(',')
|
492
|
-
uri = segments_uri + "/updateClients?deleteExtraInSegments=#{segments_to_delete_in}"
|
493
|
-
res = client.post(uri, updateClients: { items: payload })
|
494
|
-
else
|
495
|
-
res = client.post(segments_uri + '/updateClients', updateClients: { items: payload })
|
496
|
-
end
|
497
|
-
data = GoodData::Helpers.get_path(res, ['updateClientsResponse'])
|
498
|
-
if data
|
499
|
-
result = data.flat_map { |k, v| v.map { |h| GoodData::Helpers.symbolize_keys(h.merge('type' => k)) } }
|
500
|
-
result.select { |r| r[:status] == 'DELETED' }.peach { |r| r[:originalProject] && client.delete(r[:originalProject]) } if delete_projects
|
501
|
-
result
|
502
|
-
else
|
503
|
-
[]
|
455
|
+
data.group_by(&:data_product_id).each do |data_product_id, client_update_data|
|
456
|
+
data_product = data_products(data_product_id)
|
457
|
+
data_product.update_clients(client_update_data, options)
|
504
458
|
end
|
505
459
|
end
|
506
460
|
|
@@ -108,6 +108,7 @@ module GoodData
|
|
108
108
|
d[:id] = date_dim['dateDimension']['name']
|
109
109
|
d[:title] = date_dim['dateDimension']['title']
|
110
110
|
d[:urn] = date_dim['dateDimension']['urn']
|
111
|
+
d[:identifier_prefix] = date_dim['dateDimension']['identifierPrefix']
|
111
112
|
end
|
112
113
|
end
|
113
114
|
|
@@ -125,6 +126,7 @@ module GoodData
|
|
125
126
|
f[:description] = fact['fact']['description'] if fact['fact']['description']
|
126
127
|
f[:folder] = fact['fact']['folder']
|
127
128
|
f[:gd_data_type] = fact['fact']['dataType'] || GoodData::Model::DEFAULT_FACT_DATATYPE
|
129
|
+
f[:restricted] = fact['fact']['restricted'] if fact['fact']['restricted']
|
128
130
|
end
|
129
131
|
end
|
130
132
|
end
|
@@ -72,7 +72,7 @@ module GoodData
|
|
72
72
|
end
|
73
73
|
end
|
74
74
|
|
75
|
-
if result.empty?
|
75
|
+
if result.to_s.empty?
|
76
76
|
ReportDataResult.new(data: [], top: 0, left: 0)
|
77
77
|
else
|
78
78
|
ReportDataResult.from_xtab(result)
|
@@ -144,10 +144,16 @@ module GoodData
|
|
144
144
|
|
145
145
|
# Computes the report and returns the result. If it is not computable returns nil.
|
146
146
|
#
|
147
|
+
# @option options [Time] :time Force the platform to simutale the result at this time
|
147
148
|
# @return [GoodData::DataResult] Returns the result
|
148
149
|
def execute(options = {})
|
150
|
+
time = options[:time]
|
151
|
+
|
152
|
+
report_req = { 'report' => uri }
|
153
|
+
report_req['timestamp'] = time.to_i if time
|
154
|
+
|
149
155
|
fail 'You have to save the report before executing. If you do not want to do that please use GoodData::ReportDefinition' unless saved?
|
150
|
-
result = client.post
|
156
|
+
result = client.post "/gdc/projects/#{project.pid}/execute", 'report_req' => report_req
|
151
157
|
GoodData::Report.data_result(result, options.merge(client: client))
|
152
158
|
end
|
153
159
|
|
@@ -163,7 +169,7 @@ module GoodData
|
|
163
169
|
#
|
164
170
|
# @return [String] Returns data
|
165
171
|
def export(format, options = {})
|
166
|
-
result = client.post(
|
172
|
+
result = client.post("/gdc/projects/#{project.pid}/execute", 'report_req' => { 'report' => uri })
|
167
173
|
result1 = client.post('/gdc/exporter/executor', :result_req => { :format => format, :result => result })
|
168
174
|
client.poll_on_code(result1['uri'], options.merge(process: false))
|
169
175
|
end
|
@@ -228,7 +228,7 @@ module GoodData
|
|
228
228
|
pars = {
|
229
229
|
'report_req' => { 'reportDefinition' => uri }
|
230
230
|
}
|
231
|
-
client.post
|
231
|
+
client.post "/gdc/projects/#{project.pid}/execute", pars
|
232
232
|
else
|
233
233
|
data = {
|
234
234
|
report_req: {
|
@@ -238,7 +238,7 @@ module GoodData
|
|
238
238
|
}
|
239
239
|
}
|
240
240
|
}
|
241
|
-
uri = "/gdc/
|
241
|
+
uri = "/gdc/projects/#{project.pid}/execute"
|
242
242
|
client.post(uri, data)
|
243
243
|
end
|
244
244
|
GoodData::Report.data_result(result, opts.merge(client: client))
|
@@ -86,7 +86,7 @@ module GoodData
|
|
86
86
|
'GDC.time.day_us_noleading', # M/d/yy
|
87
87
|
]
|
88
88
|
|
89
|
-
GD_DATA_TYPES = ['BIGINT', 'DOUBLE', 'INTEGER', 'INT', /^VARCHAR\(\d{
|
89
|
+
GD_DATA_TYPES = ['BIGINT', 'DOUBLE', 'INTEGER', 'INT', /^VARCHAR\(([1-9]\d{0,3}|10000)\)$/i, /^DECIMAL\(\d{1,3},\s*\d{1,3}\)$/i]
|
90
90
|
|
91
91
|
DEFAULT_FACT_DATATYPE = 'DECIMAL(12,2)'
|
92
92
|
DEFAULT_ATTRIBUTE_DATATYPE = 'VARCHAR(128)'
|
@@ -125,6 +125,10 @@ module GoodData
|
|
125
125
|
|
126
126
|
File.delete(deployed_path) if File.exist?(deployed_path)
|
127
127
|
|
128
|
+
if res['asyncTask']
|
129
|
+
res = client.poll_on_response(res['asyncTask']['links']['poll']) { |body| body['asyncTask'] }
|
130
|
+
end
|
131
|
+
|
128
132
|
process = client.create(Process, res, project: project)
|
129
133
|
puts HighLine.color("Deploy DONE #{path}", HighLine::GREEN) if verbose
|
130
134
|
process
|
@@ -256,39 +256,60 @@ module GoodData
|
|
256
256
|
def transfer_processes(from_project, to_project, options = {})
|
257
257
|
options = GoodData::Helpers.symbolize_keys(options)
|
258
258
|
to_project_processes = to_project.processes
|
259
|
-
from_project.processes.uniq(&:name).
|
259
|
+
result = from_project.processes.uniq(&:name).map do |process|
|
260
260
|
fail "The process name #{process.name} must be unique in transfered project #{to_project}" if to_project_processes.count { |p| p.name == process.name } > 1
|
261
|
+
next if process.type == :dataload
|
261
262
|
to_process = to_project_processes.find { |p| p.name == process.name }
|
262
263
|
|
263
|
-
if process.path
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
264
|
+
to_process = if process.path
|
265
|
+
to_process.delete if to_process
|
266
|
+
GoodData::Process.deploy_from_appstore(process.path, name: process.name, client: to_project.client, project: to_project)
|
267
|
+
else
|
268
|
+
Dir.mktmpdir('etl_transfer') do |dir|
|
269
|
+
dir = Pathname(dir)
|
270
|
+
filename = dir + 'process.zip'
|
271
|
+
File.open(filename, 'w') do |f|
|
272
|
+
f << process.download
|
273
|
+
end
|
274
|
+
|
275
|
+
if to_process
|
276
|
+
to_process.deploy(filename, type: process.type, name: process.name)
|
277
|
+
else
|
278
|
+
to_project.deploy_process(filename, type: process.type, name: process.name)
|
279
|
+
end
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
{
|
284
|
+
from: from_project.pid,
|
285
|
+
to: to_project.pid,
|
286
|
+
name: process.name,
|
287
|
+
status: to_process ? 'successful' : 'failed'
|
288
|
+
}
|
277
289
|
end
|
278
290
|
|
279
291
|
transfer_output_stage(from_project, to_project, options)
|
292
|
+
result << {
|
293
|
+
from: from_project.pid,
|
294
|
+
to: to_project.pid,
|
295
|
+
name: 'Automated Data Distribution',
|
296
|
+
status: 'successful'
|
297
|
+
}
|
280
298
|
|
281
299
|
res = (from_project.processes + to_project.processes).map { |p| [p, p.name, p.type] }
|
282
300
|
res.group_by { |x| [x[1], x[2]] }
|
283
301
|
.select { |_, procs| procs.length == 1 && procs[2] != :dataload }
|
284
302
|
.flat_map { |_, procs| procs.select { |p| p[0].project.pid == to_project.pid }.map { |p| p[0] } }
|
285
303
|
.peach(&:delete)
|
304
|
+
|
305
|
+
result.compact
|
286
306
|
end
|
287
307
|
|
288
308
|
def transfer_user_groups(from_project, to_project)
|
289
|
-
from_project.user_groups.
|
309
|
+
from_project.user_groups.map do |ug|
|
290
310
|
# migrate groups
|
291
311
|
new_group = to_project.user_groups.select { |group| group.name == ug.name }.first
|
312
|
+
new_group_status = new_group ? 'modified' : 'created'
|
292
313
|
new_group ||= UserGroup.create(:name => ug.name, :description => ug.description, :project => to_project)
|
293
314
|
new_group.project = to_project
|
294
315
|
new_group.description = ug.description
|
@@ -303,6 +324,13 @@ module GoodData
|
|
303
324
|
permission = grantee['aclEntryURI']['permission']
|
304
325
|
new_dashboard.grant(:member => new_group, :permission => permission)
|
305
326
|
end
|
327
|
+
|
328
|
+
{
|
329
|
+
from: from_project.pid,
|
330
|
+
to: to_project.pid,
|
331
|
+
user_group: new_group.name,
|
332
|
+
status: new_group_status
|
333
|
+
}
|
306
334
|
end
|
307
335
|
end
|
308
336
|
|
@@ -314,9 +342,14 @@ module GoodData
|
|
314
342
|
# the master, client in which case we take its project, string in which
|
315
343
|
# case we treat is as an project object or directly project.
|
316
344
|
def transfer_schedules(from_project, to_project)
|
317
|
-
|
318
|
-
|
319
|
-
|
345
|
+
to_project_processes = to_project.processes.sort_by(&:name)
|
346
|
+
from_project_processes = from_project.processes.sort_by(&:name)
|
347
|
+
|
348
|
+
GoodData.logger.debug("Processes in from project #{from_project.pid}: #{from_project_processes.map(&:name).join(', ')}")
|
349
|
+
GoodData.logger.debug("Processes in to project #{to_project.pid}: #{to_project_processes.map(&:name).join(', ')}")
|
350
|
+
|
351
|
+
cache = to_project_processes
|
352
|
+
.zip(from_project_processes)
|
320
353
|
.flat_map do |remote, local|
|
321
354
|
local.schedules.map do |schedule|
|
322
355
|
[remote, local, schedule]
|
@@ -574,10 +607,11 @@ module GoodData
|
|
574
607
|
#
|
575
608
|
# @return [GoodData::ProjectRole] Project role if found
|
576
609
|
def blueprint(options = {})
|
577
|
-
|
610
|
+
options = { include_ca: true }.merge(options)
|
611
|
+
result = client.get("/gdc/projects/#{pid}/model/view", params: { includeDeprecated: true, includeGrain: true, includeCA: options[:include_ca] })
|
578
612
|
polling_url = result['asyncTask']['link']['poll']
|
579
613
|
model = client.poll_on_code(polling_url, options)
|
580
|
-
bp = GoodData::Model::FromWire.from_wire(model,
|
614
|
+
bp = GoodData::Model::FromWire.from_wire(model, options)
|
581
615
|
bp.title = title
|
582
616
|
bp
|
583
617
|
end
|
@@ -730,6 +764,7 @@ module GoodData
|
|
730
764
|
def data_permissions(id = :all)
|
731
765
|
GoodData::MandatoryUserFilter[id, client: client, project: self]
|
732
766
|
end
|
767
|
+
alias_method :user_filters, :data_permissions
|
733
768
|
|
734
769
|
# Deletes project
|
735
770
|
def delete
|
@@ -1075,7 +1110,7 @@ module GoodData
|
|
1075
1110
|
# @param [String] key key of the value to be stored
|
1076
1111
|
# @return [String] val value to be stored
|
1077
1112
|
def set_metadata(key, val)
|
1078
|
-
GoodData::ProjectMetadata[key, client: client, project: self
|
1113
|
+
GoodData::ProjectMetadata.[]=(key, { client: client, project: self }, val)
|
1079
1114
|
end
|
1080
1115
|
|
1081
1116
|
# Helper for getting metrics of a project
|
@@ -1185,7 +1220,7 @@ module GoodData
|
|
1185
1220
|
:partialMDImport => {
|
1186
1221
|
:token => token,
|
1187
1222
|
:overwriteNewer => '1',
|
1188
|
-
:updateLDMObjects => '
|
1223
|
+
:updateLDMObjects => '1',
|
1189
1224
|
:importAttributeProperties => '1'
|
1190
1225
|
}
|
1191
1226
|
}
|
@@ -1512,18 +1547,6 @@ module GoodData
|
|
1512
1547
|
data['links']['self'] if data && data['links'] && data['links']['self']
|
1513
1548
|
end
|
1514
1549
|
|
1515
|
-
# List of user filters within this project
|
1516
|
-
#
|
1517
|
-
# @return [Array<GoodData::MandatoryUserFilter>] List of mandatory user
|
1518
|
-
def user_filters
|
1519
|
-
url = "/gdc/md/#{pid}/userfilters"
|
1520
|
-
|
1521
|
-
tmp = client.get(url)
|
1522
|
-
tmp['userFilters']['items'].pmap do |filter|
|
1523
|
-
client.create(GoodData::MandatoryUserFilter, filter, project: self)
|
1524
|
-
end
|
1525
|
-
end
|
1526
|
-
|
1527
1550
|
# List of users in project
|
1528
1551
|
#
|
1529
1552
|
#
|
@@ -127,6 +127,19 @@ module GoodData
|
|
127
127
|
preference = GoodData::Helpers.symbolize_keys(opts[:update_preference] || {})
|
128
128
|
preference = Hash[preference.map { |k, v| [k, GoodData::Helpers.to_boolean(v)] }]
|
129
129
|
|
130
|
+
# will use new parameters instead of the old ones
|
131
|
+
if preference.empty? || [:allow_cascade_drops, :keep_data].any? { |k| preference.key?(k) }
|
132
|
+
if [:cascade_drops, :preserve_data].any? { |k| preference.key?(k) }
|
133
|
+
fail "Please do not mix old parameters (:cascade_drops, :preserve_data) with the new ones (:allow_cascade_drops, :keep_data)."
|
134
|
+
end
|
135
|
+
preference = { allow_cascade_drops: false, keep_data: true }.merge(preference)
|
136
|
+
|
137
|
+
new_preference = {}
|
138
|
+
new_preference[:cascade_drops] = false unless preference[:allow_cascade_drops]
|
139
|
+
new_preference[:preserve_data] = true if preference[:keep_data]
|
140
|
+
preference = new_preference
|
141
|
+
end
|
142
|
+
|
130
143
|
# first is cascadeDrops, second is preserveData
|
131
144
|
rules = [
|
132
145
|
{ priority: 1, cascade_drops: false, preserve_data: true },
|
@@ -15,10 +15,15 @@ require_relative '../mixins/uri_getter'
|
|
15
15
|
|
16
16
|
module GoodData
|
17
17
|
class Segment < Rest::Resource
|
18
|
-
SYNCHRONIZE_URI = '/gdc/domains/%s/segments/%s/synchronizeClients'
|
18
|
+
SYNCHRONIZE_URI = '/gdc/domains/%s/dataproducts/%s/segments/%s/synchronizeClients'
|
19
|
+
|
20
|
+
#
|
21
|
+
ProvisioningResult = Struct.new('ProvisioningResult', :id, :status, :project_uri, :error)
|
19
22
|
|
20
23
|
attr_accessor :domain
|
21
24
|
|
25
|
+
attr_writer :data_product
|
26
|
+
|
22
27
|
data_property_reader 'id'
|
23
28
|
|
24
29
|
include Mixin::Links
|
@@ -43,10 +48,12 @@ module GoodData
|
|
43
48
|
client = domain.client
|
44
49
|
fail ArgumentError, 'No client specified' if client.nil?
|
45
50
|
|
51
|
+
data_product = opts[:data_product]
|
52
|
+
|
46
53
|
if id == :all
|
47
54
|
GoodData::Segment.all(opts)
|
48
55
|
else
|
49
|
-
result = client.get(domain
|
56
|
+
result = client.get(base_uri(domain, data_product) + "/segments/#{CGI.escape(id)}")
|
50
57
|
client.create(GoodData::Segment, result.merge('domain' => domain))
|
51
58
|
end
|
52
59
|
end
|
@@ -60,8 +67,17 @@ module GoodData
|
|
60
67
|
fail 'Domain has to be passed in options' unless domain
|
61
68
|
client = domain.client
|
62
69
|
|
63
|
-
|
64
|
-
|
70
|
+
data_product = opts[:data_product]
|
71
|
+
results = client.get(base_uri(domain, data_product) + '/segments')
|
72
|
+
GoodData::Helpers.get_path(results, %w(segments items)).map { |i| client.create(GoodData::Segment, i, domain: domain) }
|
73
|
+
end
|
74
|
+
|
75
|
+
def base_uri(domain, data_product)
|
76
|
+
if data_product
|
77
|
+
GoodData::DataProduct::ONE_DATA_PRODUCT_PATH % { domain_name: domain.name, id: data_product.data_product_id }
|
78
|
+
else
|
79
|
+
domain.segments_uri
|
80
|
+
end
|
65
81
|
end
|
66
82
|
|
67
83
|
# Creates new segment from parameters passed
|
@@ -71,9 +87,9 @@ module GoodData
|
|
71
87
|
# @return [GoodData::Segment] New Segment instance
|
72
88
|
def create(data = {}, options = {})
|
73
89
|
segment_id = data[:segment_id]
|
74
|
-
fail '
|
90
|
+
fail 'segment_id has to be provided' if segment_id.blank?
|
75
91
|
client = options[:client]
|
76
|
-
segment = client.create(GoodData::Segment, GoodData::Helpers.stringify_keys(SEGMENT_TEMPLATE)
|
92
|
+
segment = client.create(GoodData::Segment, GoodData::Helpers.stringify_keys(SEGMENT_TEMPLATE), options)
|
77
93
|
segment.tap do |s|
|
78
94
|
s.segment_id = segment_id
|
79
95
|
s.master_project = data[:master_project]
|
@@ -84,9 +100,19 @@ module GoodData
|
|
84
100
|
def initialize(data)
|
85
101
|
super
|
86
102
|
@domain = data.delete('domain')
|
103
|
+
@data_product = nil
|
87
104
|
@json = data
|
88
105
|
end
|
89
106
|
|
107
|
+
def data_product
|
108
|
+
if @data_product
|
109
|
+
@data_product
|
110
|
+
else
|
111
|
+
json = client.get(data['links']['dataProduct'])
|
112
|
+
@data_product = client.create(GoodData::DataProduct, json)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
90
116
|
# Segment id getter for the Segment. Called segment_id since id is a reserved word in ruby world
|
91
117
|
#
|
92
118
|
# @return [String] Segment id
|
@@ -159,17 +185,17 @@ module GoodData
|
|
159
185
|
if uri
|
160
186
|
client.put(uri, json)
|
161
187
|
else
|
162
|
-
res = client.post(domain
|
188
|
+
res = client.post(self.class.base_uri(domain, @data_product ? data_product : nil) + '/segments', json)
|
163
189
|
@json = res
|
164
190
|
end
|
165
191
|
self
|
166
192
|
end
|
167
193
|
|
168
|
-
# Runs async process that walks
|
194
|
+
# Runs async process that walks through segments and provisions projects if necessary.
|
169
195
|
#
|
170
196
|
# @return [Array] Returns array of results
|
171
197
|
def synchronize_clients
|
172
|
-
sync_uri = SYNCHRONIZE_URI % [domain.obj_id, id]
|
198
|
+
sync_uri = SYNCHRONIZE_URI % [domain.obj_id, data_product.data_product_id, id]
|
173
199
|
res = client.post sync_uri, nil
|
174
200
|
|
175
201
|
# wait until the instance is created
|
@@ -180,7 +206,7 @@ module GoodData
|
|
180
206
|
client.create(ClientSynchronizationResult, res)
|
181
207
|
end
|
182
208
|
|
183
|
-
def synchronize_processes(projects = []
|
209
|
+
def synchronize_processes(projects = [])
|
184
210
|
projects = [projects] unless projects.is_a?(Array)
|
185
211
|
projects = projects.map do |p|
|
186
212
|
p = p.pid if p.respond_to?('pid')
|
@@ -198,7 +224,8 @@ module GoodData
|
|
198
224
|
}
|
199
225
|
end
|
200
226
|
|
201
|
-
uri = '/gdc/internal/lcm/domains/%
|
227
|
+
uri = '/gdc/internal/lcm/domains/%{domain}/dataproducts/%{dataproduct}/segments/%{segment}/syncProcesses'
|
228
|
+
uri = uri % { domain: domain.name, dataproduct: data_product.data_product_id, segment: segment_id }
|
202
229
|
res = client.post(uri, body)
|
203
230
|
|
204
231
|
client.poll_on_response(GoodData::Helpers.get_path(res, %w(asyncTask link poll)), sleep_interval: 1) do |r|
|
@@ -216,11 +243,31 @@ module GoodData
|
|
216
243
|
self
|
217
244
|
rescue RestClient::BadRequest => e
|
218
245
|
payload = GoodData::Helpers.parse_http_exception(e)
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
246
|
+
e = SegmentNotEmpty if GoodData::Helpers.get_path(payload) == 'gdc.c4.conflict.domain.segment.contains_clients'
|
247
|
+
raise e
|
248
|
+
end
|
249
|
+
|
250
|
+
def provision_client_projects(segments = [])
|
251
|
+
body = {
|
252
|
+
provisionClientProjects: {
|
253
|
+
segments: segments.empty? ? [segment_id] : segments
|
254
|
+
}
|
255
|
+
}
|
256
|
+
res = client.post(GoodData::DataProduct::ONE_DATA_PRODUCT_PATH % { domain_name: domain.name, id: data_product.data_product_id } + '/provisionClientProjects', body)
|
257
|
+
res = client.poll_on_code(res['asyncTask']['links']['poll'])
|
258
|
+
failed_count = GoodData::Helpers.get_path(res, %w(clientProjectProvisioningResult failed count), 0)
|
259
|
+
created_count = GoodData::Helpers.get_path(res, %w(clientProjectProvisioningResult created count), 0)
|
260
|
+
return Enumerator.new([]) if (failed_count + created_count).zero?
|
261
|
+
Enumerator.new do |y|
|
262
|
+
uri = GoodData::Helpers.get_path(res, %w(clientProjectProvisioningResult links details))
|
263
|
+
loop do
|
264
|
+
result = client.get(uri)
|
265
|
+
(GoodData::Helpers.get_path(result, %w(clientProjectProvisioningResultDetails items)) || []).each do |item|
|
266
|
+
y << ProvisioningResult.new(item['id'], item['status'], item['project'], item['error'])
|
267
|
+
end
|
268
|
+
uri = GoodData::Helpers.get_path(res, %w(clientProjectProvisioningResultDetails paging next))
|
269
|
+
break if uri.nil?
|
270
|
+
end
|
224
271
|
end
|
225
272
|
end
|
226
273
|
end
|