gooddata 0.6.18 → 0.6.19
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 +4 -4
- data/.gitignore +2 -1
- data/.travis.yml +8 -19
- data/Guardfile +5 -0
- data/README.md +1 -3
- data/bin/gooddata +1 -1
- data/gooddata.gemspec +6 -4
- data/lib/gooddata.rb +1 -1
- data/lib/gooddata/bricks/middleware/aws_middleware.rb +24 -0
- data/lib/gooddata/cli/commands/console_cmd.rb +1 -1
- data/lib/gooddata/cli/commands/project_cmd.rb +29 -9
- data/lib/gooddata/cli/hooks.rb +9 -3
- data/lib/gooddata/commands/datawarehouse.rb +1 -7
- data/lib/gooddata/commands/project.rb +4 -3
- data/lib/gooddata/core/logging.rb +14 -2
- data/lib/gooddata/exceptions/execution_limit_exceeded.rb +9 -0
- data/lib/gooddata/exceptions/uncomputable_report.rb +8 -0
- data/lib/gooddata/exceptions/validation_error.rb +1 -1
- data/lib/gooddata/goodzilla/goodzilla.rb +5 -1
- data/lib/gooddata/helpers/data_helper.rb +40 -9
- data/lib/gooddata/mixins/md_finders.rb +35 -0
- data/lib/gooddata/models/blueprint/anchor_field.rb +46 -0
- data/lib/gooddata/models/blueprint/attribute_field.rb +25 -0
- data/lib/gooddata/models/blueprint/blueprint.rb +7 -0
- data/lib/gooddata/models/blueprint/blueprint_field.rb +66 -0
- data/lib/gooddata/models/{dashboard_builder.rb → blueprint/dashboard_builder.rb} +0 -0
- data/lib/gooddata/models/{schema_blueprint.rb → blueprint/dataset_blueprint.rb} +176 -117
- data/lib/gooddata/models/blueprint/date_dimension.rb +10 -0
- data/lib/gooddata/models/blueprint/fact_field.rb +16 -0
- data/lib/gooddata/models/blueprint/label_field.rb +39 -0
- data/lib/gooddata/models/{project_blueprint.rb → blueprint/project_blueprint.rb} +366 -168
- data/lib/gooddata/models/blueprint/project_builder.rb +79 -0
- data/lib/gooddata/models/blueprint/reference_field.rb +39 -0
- data/lib/gooddata/models/blueprint/schema_blueprint.rb +156 -0
- data/lib/gooddata/models/blueprint/schema_builder.rb +85 -0
- data/lib/gooddata/models/{to_manifest.rb → blueprint/to_manifest.rb} +25 -20
- data/lib/gooddata/models/{to_wire.rb → blueprint/to_wire.rb} +33 -52
- data/lib/gooddata/models/datawarehouse.rb +2 -2
- data/lib/gooddata/models/domain.rb +3 -2
- data/lib/gooddata/models/execution.rb +2 -2
- data/lib/gooddata/models/execution_detail.rb +7 -2
- data/lib/gooddata/models/from_wire.rb +60 -71
- data/lib/gooddata/models/from_wire_parse.rb +125 -125
- data/lib/gooddata/models/metadata.rb +14 -0
- data/lib/gooddata/models/metadata/dashboard.rb +2 -2
- data/lib/gooddata/models/metadata/label.rb +1 -1
- data/lib/gooddata/models/metadata/report.rb +6 -5
- data/lib/gooddata/models/metadata/report_definition.rb +44 -59
- data/lib/gooddata/models/model.rb +131 -43
- data/lib/gooddata/models/process.rb +13 -11
- data/lib/gooddata/models/profile.rb +12 -1
- data/lib/gooddata/models/project.rb +223 -19
- data/lib/gooddata/models/project_creator.rb +4 -15
- data/lib/gooddata/models/schedule.rb +1 -0
- data/lib/gooddata/models/user_filters/user_filter_builder.rb +2 -2
- data/lib/gooddata/rest/client.rb +18 -18
- data/lib/gooddata/rest/connection.rb +113 -94
- data/lib/gooddata/version.rb +1 -1
- data/lib/templates/project/model/model.rb.erb +15 -16
- data/spec/data/blueprints/additional_dataset_module.json +32 -0
- data/spec/data/blueprints/big_blueprint_not_pruned.json +2079 -0
- data/spec/data/blueprints/invalid_blueprint.json +103 -0
- data/spec/data/blueprints/m_n_model.json +104 -0
- data/spec/data/blueprints/model_module.json +25 -0
- data/spec/data/blueprints/test_blueprint.json +38 -0
- data/spec/data/blueprints/test_project_model_spec.json +106 -0
- data/spec/data/gd_gse_data_manifest.json +34 -34
- data/spec/data/manifests/test_blueprint.json +32 -0
- data/spec/data/{manifest_test_project.json → manifests/test_project.json} +9 -18
- data/spec/data/wire_models/test_blueprint.json +63 -0
- data/spec/data/wire_test_project.json +5 -5
- data/spec/environment/default.rb +33 -0
- data/spec/environment/develop.rb +26 -0
- data/spec/environment/environment.rb +14 -0
- data/spec/environment/hotfix.rb +17 -0
- data/spec/environment/production.rb +31 -0
- data/spec/environment/release.rb +17 -0
- data/spec/helpers/blueprint_helper.rb +10 -7
- data/spec/helpers/cli_helper.rb +24 -22
- data/spec/helpers/connection_helper.rb +27 -25
- data/spec/helpers/crypto_helper.rb +7 -5
- data/spec/helpers/csv_helper.rb +5 -3
- data/spec/helpers/process_helper.rb +15 -10
- data/spec/helpers/project_helper.rb +40 -33
- data/spec/helpers/schedule_helper.rb +15 -9
- data/spec/helpers/spec_helper.rb +11 -0
- data/spec/integration/blueprint_updates_spec.rb +93 -0
- data/spec/integration/command_datawarehouse_spec.rb +2 -1
- data/spec/integration/command_projects_spec.rb +9 -8
- data/spec/integration/create_from_template_spec.rb +1 -1
- data/spec/integration/create_project_spec.rb +1 -1
- data/spec/integration/full_process_schedule_spec.rb +1 -1
- data/spec/integration/full_project_spec.rb +91 -30
- data/spec/integration/over_to_user_filters_spec.rb +24 -28
- data/spec/integration/partial_md_export_import_spec.rb +4 -4
- data/spec/integration/project_spec.rb +1 -1
- data/spec/integration/rest_spec.rb +1 -1
- data/spec/integration/user_filters_spec.rb +19 -24
- data/spec/integration/variables_spec.rb +7 -9
- data/spec/logging_in_logging_out_spec.rb +1 -1
- data/spec/spec_helper.rb +10 -1
- data/spec/unit/bricks/middleware/aws_middelware_spec.rb +47 -0
- data/spec/unit/core/connection_spec.rb +2 -2
- data/spec/unit/core/logging_spec.rb +12 -4
- data/spec/unit/helpers/data_helper_spec.rb +60 -0
- data/spec/unit/models/blueprint/attributes_spec.rb +24 -0
- data/spec/unit/models/blueprint/dataset_spec.rb +116 -0
- data/spec/unit/models/blueprint/labels_spec.rb +39 -0
- data/spec/unit/models/blueprint/project_blueprint_spec.rb +643 -0
- data/spec/unit/models/blueprint/reference_spec.rb +24 -0
- data/spec/unit/models/{schema_builder_spec.rb → blueprint/schema_builder_spec.rb} +12 -4
- data/spec/unit/models/blueprint/to_wire_spec.rb +169 -0
- data/spec/unit/models/domain_spec.rb +13 -2
- data/spec/unit/models/from_wire_spec.rb +277 -98
- data/spec/unit/models/metadata_spec.rb +22 -4
- data/spec/unit/models/model_spec.rb +49 -39
- data/spec/unit/models/profile_spec.rb +1 -0
- data/spec/unit/models/project_spec.rb +7 -7
- data/spec/unit/models/schedule_spec.rb +20 -0
- data/spec/unit/models/to_manifest_spec.rb +31 -11
- data/spec/unit/rest/polling_spec.rb +86 -0
- metadata +102 -30
- data/lib/gooddata/models/project_builder.rb +0 -136
- data/lib/gooddata/models/schema_builder.rb +0 -77
- data/out.txt +0 -0
- data/spec/data/additional_dataset_module.json +0 -18
- data/spec/data/blueprint_invalid.json +0 -38
- data/spec/data/m_n_model/blueprint.json +0 -76
- data/spec/data/model_module.json +0 -18
- data/spec/data/test_project_model_spec.json +0 -76
- data/spec/unit/models/attribute_column_spec.rb +0 -7
- data/spec/unit/models/project_blueprint_spec.rb +0 -239
- data/spec/unit/models/to_wire_spec.rb +0 -71
|
@@ -67,6 +67,8 @@ module GoodData
|
|
|
67
67
|
begin
|
|
68
68
|
res = GoodData::Process.deploy(dir, options.merge(:files_to_exclude => params))
|
|
69
69
|
block.call(res)
|
|
70
|
+
rescue => e
|
|
71
|
+
puts e.inspect
|
|
70
72
|
ensure
|
|
71
73
|
res.delete if res
|
|
72
74
|
end
|
|
@@ -142,20 +144,20 @@ module GoodData
|
|
|
142
144
|
|
|
143
145
|
private
|
|
144
146
|
|
|
145
|
-
def
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
File.open(
|
|
150
|
-
|
|
151
|
-
yield zipfile
|
|
152
|
-
end
|
|
147
|
+
def with_zip(opts = {})
|
|
148
|
+
Tempfile.open('deploy-graph-archive') do |temp|
|
|
149
|
+
zip_filename = temp.path
|
|
150
|
+
File.open(zip_filename, 'w') do |zip|
|
|
151
|
+
Zip::File.open(zip.path, Zip::File::CREATE) do |zipfile|
|
|
152
|
+
yield zipfile
|
|
153
153
|
end
|
|
154
|
-
client.upload_to_user_webdav(temp.path, opts)
|
|
155
|
-
temp.path
|
|
156
154
|
end
|
|
155
|
+
client.upload_to_user_webdav(temp.path, opts)
|
|
156
|
+
temp.path
|
|
157
157
|
end
|
|
158
|
+
end
|
|
158
159
|
|
|
160
|
+
def zip_and_upload(path, files_to_exclude, opts = {})
|
|
159
161
|
puts 'Creating package for upload'
|
|
160
162
|
if !path.directory? && (path.extname == '.grf' || path.extname == '.rb')
|
|
161
163
|
with_zip(opts) do |zipfile|
|
|
@@ -260,7 +262,7 @@ module GoodData
|
|
|
260
262
|
def execute(executable, options = {})
|
|
261
263
|
result = start_execution(executable, options)
|
|
262
264
|
begin
|
|
263
|
-
client.poll_on_code(result['executionTask']['links']['poll'])
|
|
265
|
+
client.poll_on_code(result['executionTask']['links']['poll'], options)
|
|
264
266
|
rescue RestClient::RequestFailed => e
|
|
265
267
|
raise(e)
|
|
266
268
|
ensure
|
|
@@ -313,7 +313,7 @@ module GoodData
|
|
|
313
313
|
|
|
314
314
|
# Saves object if dirty, clears dirty flag
|
|
315
315
|
def save!
|
|
316
|
-
if @dirty
|
|
316
|
+
if @dirty
|
|
317
317
|
raw = @json.dup
|
|
318
318
|
raw['accountSetting'].delete('login')
|
|
319
319
|
|
|
@@ -323,6 +323,7 @@ module GoodData
|
|
|
323
323
|
@dirty = false
|
|
324
324
|
end
|
|
325
325
|
end
|
|
326
|
+
self
|
|
326
327
|
end
|
|
327
328
|
|
|
328
329
|
# Gets the preferred timezone
|
|
@@ -381,6 +382,16 @@ module GoodData
|
|
|
381
382
|
@json['accountSetting']['ssoProvider'] = an_sso_provider
|
|
382
383
|
end
|
|
383
384
|
|
|
385
|
+
def authentication_modes
|
|
386
|
+
@json['accountSetting']['authenticationModes'].map { |x| x.downcase.to_sym }
|
|
387
|
+
end
|
|
388
|
+
|
|
389
|
+
def authentication_modes=(modes)
|
|
390
|
+
modes = Array(modes)
|
|
391
|
+
@dirty = true
|
|
392
|
+
@json['accountSetting']['authenticationModes'] = modes.map { |x| x.to_s.upcase }
|
|
393
|
+
end
|
|
394
|
+
|
|
384
395
|
def to_hash
|
|
385
396
|
tmp = content.merge(uri: uri).symbolize_keys
|
|
386
397
|
[
|
|
@@ -16,6 +16,7 @@ require_relative '../mixins/rest_resource'
|
|
|
16
16
|
|
|
17
17
|
require_relative 'process'
|
|
18
18
|
require_relative 'project_role'
|
|
19
|
+
require_relative 'blueprint/blueprint'
|
|
19
20
|
|
|
20
21
|
module GoodData
|
|
21
22
|
class Project < GoodData::Rest::Resource
|
|
@@ -32,7 +33,8 @@ module GoodData
|
|
|
32
33
|
},
|
|
33
34
|
'content' => {
|
|
34
35
|
'guidedNavigation' => 1,
|
|
35
|
-
'driver' => 'Pg'
|
|
36
|
+
'driver' => 'Pg',
|
|
37
|
+
'environment' => 'PRODUCTION'
|
|
36
38
|
}
|
|
37
39
|
}
|
|
38
40
|
}
|
|
@@ -90,8 +92,12 @@ module GoodData
|
|
|
90
92
|
d['project']['meta']['summary'] = data[:summary] if data[:summary]
|
|
91
93
|
d['project']['meta']['projectTemplate'] = data[:template] if data[:template]
|
|
92
94
|
d['project']['content']['guidedNavigation'] = data[:guided_navigation] if data[:guided_navigation]
|
|
93
|
-
|
|
95
|
+
|
|
96
|
+
token = data[:auth_token] || data[:token]
|
|
97
|
+
|
|
98
|
+
d['project']['content']['authorizationToken'] = token if token
|
|
94
99
|
d['project']['content']['driver'] = data[:driver] if data[:driver]
|
|
100
|
+
d['project']['content']['environment'] = data[:environment] if data[:environment]
|
|
95
101
|
end
|
|
96
102
|
c.create(Project, new_data)
|
|
97
103
|
end
|
|
@@ -108,7 +114,7 @@ module GoodData
|
|
|
108
114
|
c = client(opts)
|
|
109
115
|
fail ArgumentError, 'No :client specified' if c.nil?
|
|
110
116
|
|
|
111
|
-
auth_token = opts[:auth_token]
|
|
117
|
+
auth_token = opts[:auth_token] || opts[:token]
|
|
112
118
|
fail ArgumentError, 'You have to provide your token for creating projects as :auth_token parameter' if auth_token.nil? || auth_token.empty?
|
|
113
119
|
|
|
114
120
|
project = create_object(opts)
|
|
@@ -140,7 +146,7 @@ module GoodData
|
|
|
140
146
|
end
|
|
141
147
|
|
|
142
148
|
def create_from_blueprint(blueprint, options = {})
|
|
143
|
-
GoodData::Model::ProjectCreator.migrate(options.merge(spec: blueprint,
|
|
149
|
+
GoodData::Model::ProjectCreator.migrate(options.merge(spec: blueprint, client: GoodData.connection))
|
|
144
150
|
end
|
|
145
151
|
|
|
146
152
|
# Takes one CSV line and creates hash from data extracted
|
|
@@ -173,6 +179,7 @@ module GoodData
|
|
|
173
179
|
GoodData::Metric.xcreate(options[:expression], metric.merge(options.merge(default)))
|
|
174
180
|
end
|
|
175
181
|
end
|
|
182
|
+
|
|
176
183
|
alias_method :create_metric, :add_metric
|
|
177
184
|
|
|
178
185
|
# Creates new instance of report in context of project
|
|
@@ -183,6 +190,7 @@ module GoodData
|
|
|
183
190
|
rep = GoodData::Report.create(options.merge(client: client, project: self))
|
|
184
191
|
rep.save
|
|
185
192
|
end
|
|
193
|
+
|
|
186
194
|
alias_method :create_report, :add_report
|
|
187
195
|
|
|
188
196
|
# Creates new instance of report definition in context of project
|
|
@@ -196,6 +204,7 @@ module GoodData
|
|
|
196
204
|
rd.project = self
|
|
197
205
|
rd.save
|
|
198
206
|
end
|
|
207
|
+
|
|
199
208
|
alias_method :create_report_definition, :add_report_definition
|
|
200
209
|
|
|
201
210
|
# Returns an indication whether current user is admin in this project
|
|
@@ -213,6 +222,14 @@ module GoodData
|
|
|
213
222
|
GoodData::Attribute[id, project: self, client: client]
|
|
214
223
|
end
|
|
215
224
|
|
|
225
|
+
def attribute_by_identifier(identifier)
|
|
226
|
+
GoodData::Attribute.find_first_by_identifier(identifier, project: self, client: client)
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
def attributes_by_identifier(identifier)
|
|
230
|
+
GoodData::Attribute.find_by_identifier(identifier, project: self, client: client)
|
|
231
|
+
end
|
|
232
|
+
|
|
216
233
|
def attribute_by_title(title)
|
|
217
234
|
GoodData::Attribute.find_first_by_title(title, project: self, client: client)
|
|
218
235
|
end
|
|
@@ -224,11 +241,13 @@ module GoodData
|
|
|
224
241
|
# Gets project blueprint from the server
|
|
225
242
|
#
|
|
226
243
|
# @return [GoodData::ProjectRole] Project role if found
|
|
227
|
-
def blueprint
|
|
244
|
+
def blueprint(options = {})
|
|
228
245
|
result = client.get("/gdc/projects/#{pid}/model/view")
|
|
229
246
|
polling_url = result['asyncTask']['link']['poll']
|
|
230
|
-
model = client.poll_on_code(polling_url)
|
|
231
|
-
GoodData::Model::FromWire.from_wire(model)
|
|
247
|
+
model = client.poll_on_code(polling_url, options)
|
|
248
|
+
bp = GoodData::Model::FromWire.from_wire(model)
|
|
249
|
+
bp.title = title
|
|
250
|
+
bp
|
|
232
251
|
end
|
|
233
252
|
|
|
234
253
|
# Returns web interface URI of project
|
|
@@ -312,7 +331,7 @@ module GoodData
|
|
|
312
331
|
#
|
|
313
332
|
# @param export_token [String] Export token of the package to be imported
|
|
314
333
|
# @return [Project] current project
|
|
315
|
-
def import_clone(export_token)
|
|
334
|
+
def import_clone(export_token, options = {})
|
|
316
335
|
import = {
|
|
317
336
|
:importProject => {
|
|
318
337
|
:token => export_token
|
|
@@ -321,7 +340,7 @@ module GoodData
|
|
|
321
340
|
|
|
322
341
|
result = client.post("/gdc/md/#{obj_id}/maintenance/import", import)
|
|
323
342
|
status_url = result['uri']
|
|
324
|
-
client.poll_on_response(status_url) do |body|
|
|
343
|
+
client.poll_on_response(status_url, options) do |body|
|
|
325
344
|
body['taskState']['status'] == 'RUNNING'
|
|
326
345
|
end
|
|
327
346
|
self
|
|
@@ -342,6 +361,7 @@ module GoodData
|
|
|
342
361
|
def create_variable(data)
|
|
343
362
|
GoodData::Variable.create(data, client: client, project: self)
|
|
344
363
|
end
|
|
364
|
+
|
|
345
365
|
# Helper for getting dashboards of a project
|
|
346
366
|
#
|
|
347
367
|
# @param id [String | Number | Object] Anything that you can pass to GoodData::Dashboard[id]
|
|
@@ -383,12 +403,12 @@ module GoodData
|
|
|
383
403
|
#
|
|
384
404
|
# @param dml [String] DML expression
|
|
385
405
|
# @return [Hash] Result of executing DML
|
|
386
|
-
def execute_dml(dml)
|
|
406
|
+
def execute_dml(dml, options = {})
|
|
387
407
|
uri = "/gdc/md/#{pid}/dml/manage"
|
|
388
408
|
result = client.post(uri, manage: { maql: dml })
|
|
389
409
|
polling_uri = result['uri']
|
|
390
410
|
|
|
391
|
-
client.poll_on_response(polling_uri) do |body|
|
|
411
|
+
client.poll_on_response(polling_uri, options) do |body|
|
|
392
412
|
body && body['taskState'] && body['taskState']['status'] == 'WAIT'
|
|
393
413
|
end
|
|
394
414
|
end
|
|
@@ -397,13 +417,13 @@ module GoodData
|
|
|
397
417
|
#
|
|
398
418
|
# @param maql [String] MAQL expression
|
|
399
419
|
# @return [Hash] Result of executing MAQL
|
|
400
|
-
def execute_maql(maql)
|
|
420
|
+
def execute_maql(maql, options = {})
|
|
401
421
|
ldm_links = client.get(md[GoodData::Model::LDM_CTG])
|
|
402
422
|
ldm_uri = Links.new(ldm_links)[GoodData::Model::LDM_MANAGE_CTG]
|
|
403
423
|
response = client.post(ldm_uri, manage: { maql: maql })
|
|
404
424
|
polling_uri = response['entries'].first['link']
|
|
405
425
|
|
|
406
|
-
client.poll_on_response(polling_uri) do |body|
|
|
426
|
+
client.poll_on_response(polling_uri, options) do |body|
|
|
407
427
|
body && body['wTaskStatus'] && body['wTaskStatus']['status'] == 'RUNNING'
|
|
408
428
|
end
|
|
409
429
|
end
|
|
@@ -524,6 +544,10 @@ module GoodData
|
|
|
524
544
|
GoodData.download_from_project_webdav(file, where, project: self)
|
|
525
545
|
end
|
|
526
546
|
|
|
547
|
+
def environment
|
|
548
|
+
json['project']['content']['environment']
|
|
549
|
+
end
|
|
550
|
+
|
|
527
551
|
# Gets user by its email, full_name, login or uri
|
|
528
552
|
alias_method :member, :get_user
|
|
529
553
|
|
|
@@ -717,7 +741,7 @@ module GoodData
|
|
|
717
741
|
polling_url = result['partialMDArtifact']['status']['uri']
|
|
718
742
|
token = result['partialMDArtifact']['token']
|
|
719
743
|
|
|
720
|
-
polling_result = client.poll_on_response(polling_url) do |body|
|
|
744
|
+
polling_result = client.poll_on_response(polling_url, options) do |body|
|
|
721
745
|
body['wTaskStatus'] && body['wTaskStatus']['status'] == 'RUNNING'
|
|
722
746
|
end
|
|
723
747
|
|
|
@@ -734,7 +758,7 @@ module GoodData
|
|
|
734
758
|
result = client.post("#{target_project.md['maintenance']}/partialmdimport", import_payload)
|
|
735
759
|
polling_url = result['uri']
|
|
736
760
|
|
|
737
|
-
client.poll_on_response(polling_url) do |body|
|
|
761
|
+
client.poll_on_response(polling_url, options) do |body|
|
|
738
762
|
body['wTaskStatus'] && body['wTaskStatus']['status'] == 'RUNNING'
|
|
739
763
|
end
|
|
740
764
|
|
|
@@ -789,6 +813,173 @@ module GoodData
|
|
|
789
813
|
self
|
|
790
814
|
end
|
|
791
815
|
|
|
816
|
+
DEFAULT_REPLACE_DATE_DIMENSION_OPTIONS = {
|
|
817
|
+
:old => nil,
|
|
818
|
+
:new => nil,
|
|
819
|
+
:purge => false,
|
|
820
|
+
:dry_run => true,
|
|
821
|
+
:mapping => {}
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
def replace_date_dimension(opts)
|
|
825
|
+
fail ArgumentError, 'No :old dimension specified' if opts[:old].nil?
|
|
826
|
+
fail ArgumentError, 'No :new dimension specified' if opts[:new].nil?
|
|
827
|
+
|
|
828
|
+
# Merge with default options
|
|
829
|
+
opts = DEFAULT_REPLACE_DATE_DIMENSION_OPTIONS.merge(opts)
|
|
830
|
+
|
|
831
|
+
get_attribute = lambda do |attr|
|
|
832
|
+
return attr if attr.is_a?(GoodData::Attribute)
|
|
833
|
+
|
|
834
|
+
res = attribute_by_identifier(attr)
|
|
835
|
+
return res if res
|
|
836
|
+
|
|
837
|
+
attribute_by_title(attr)
|
|
838
|
+
end
|
|
839
|
+
|
|
840
|
+
if opts[:old] && opts[:new]
|
|
841
|
+
fail ArgumentError, 'You specified both :old => :new and :mapping' if opts[:mapping] && !opts[:mapping].empty?
|
|
842
|
+
|
|
843
|
+
attrs = attributes_by_title(/\(#{opts[:old]}\)$/)
|
|
844
|
+
|
|
845
|
+
attrs.each do |old_attr|
|
|
846
|
+
new_attr_title = old_attr.title.sub("(#{opts[:old]})", "(#{opts[:new]})")
|
|
847
|
+
new_attr = attribute_by_title(new_attr_title)
|
|
848
|
+
|
|
849
|
+
fail "Unable to find attribute '#{new_attr_title}' in date dimension '#{opts[:new]}'" if new_attr.nil?
|
|
850
|
+
|
|
851
|
+
opts[:mapping][old_attr] = new_attr
|
|
852
|
+
end
|
|
853
|
+
end
|
|
854
|
+
|
|
855
|
+
mufs = user_filters
|
|
856
|
+
|
|
857
|
+
# Replaces string anywhere in JSON with another string and returns back new JSON
|
|
858
|
+
json_replace = lambda do |object, old_uri, new_uri|
|
|
859
|
+
old_json = JSON.generate(object.json)
|
|
860
|
+
regexp_replace = Regexp.new(old_uri + '([^0-9])')
|
|
861
|
+
|
|
862
|
+
new_json = old_json.gsub(regexp_replace, "#{new_uri}\\1")
|
|
863
|
+
if old_json != new_json
|
|
864
|
+
object.json = JSON.parse(new_json)
|
|
865
|
+
object.save
|
|
866
|
+
end
|
|
867
|
+
object
|
|
868
|
+
end
|
|
869
|
+
|
|
870
|
+
# delete old report definitions (only the last version of each report is kept)
|
|
871
|
+
if opts[:purge]
|
|
872
|
+
GoodData.logger.info 'Purging old project definitions'
|
|
873
|
+
reports.peach(&:purge_report_of_unused_definitions!)
|
|
874
|
+
end
|
|
875
|
+
|
|
876
|
+
fail ArgumentError, 'No :mapping specified' if opts[:mapping].nil? || opts[:mapping].empty?
|
|
877
|
+
|
|
878
|
+
# Preprocess mapping, do necessary lookup
|
|
879
|
+
mapping = {}
|
|
880
|
+
opts[:mapping].each do |k, v|
|
|
881
|
+
attr_src = get_attribute.call(k)
|
|
882
|
+
attr_dest = get_attribute.call(v)
|
|
883
|
+
|
|
884
|
+
fail ArgumentError, "Unable to find attribute with identifier '#{k}'" if attr_src.nil?
|
|
885
|
+
fail ArgumentError, "Unable to find attribute with identifier '#{v}'" if attr_dest.nil?
|
|
886
|
+
|
|
887
|
+
mapping[attr_src] = attr_dest
|
|
888
|
+
end
|
|
889
|
+
|
|
890
|
+
# Iterate over all date attributes
|
|
891
|
+
mapping.each do |old_date, new_date|
|
|
892
|
+
GoodData.logger.info " replacing date attribute '#{old_date.title}' (#{old_date.uri}) with '#{new_date.title}' (#{new_date.uri})"
|
|
893
|
+
|
|
894
|
+
# For each attribute prepare list of labels to replace
|
|
895
|
+
labels_mapping = {}
|
|
896
|
+
|
|
897
|
+
old_date.labels.each do |old_label|
|
|
898
|
+
new_label_title = old_label.title.sub("(#{opts[:old]})", "(#{opts[:new]})")
|
|
899
|
+
|
|
900
|
+
# Go through all labels, label_by_title has some issues
|
|
901
|
+
new_date.json['attribute']['content']['displayForms'].each do |label_tmp|
|
|
902
|
+
if label_tmp['meta']['title'] == new_label_title
|
|
903
|
+
new_label = labels(label_tmp['meta']['uri'])
|
|
904
|
+
labels_mapping[old_label] = new_label
|
|
905
|
+
end
|
|
906
|
+
end
|
|
907
|
+
end
|
|
908
|
+
|
|
909
|
+
# Now we should have all labels for this attribute and its replacement in new date dimension
|
|
910
|
+
# First fix all affected metrics that are using this attribute
|
|
911
|
+
dependent = old_date.usedby
|
|
912
|
+
GoodData.logger.info 'Fixing metrics...'
|
|
913
|
+
dependent.each do |dependent_object|
|
|
914
|
+
next if dependent_object['category'] != 'metric'
|
|
915
|
+
|
|
916
|
+
affected_metric = metrics(dependent_object['link'])
|
|
917
|
+
|
|
918
|
+
GoodData.logger.info "Metric '#{dependent_object['title']}' (#{affected_metric.uri}) contains old date attribute '#{old_date.title}' ...replacing"
|
|
919
|
+
affected_metric.replace(old_date.uri, new_date.uri)
|
|
920
|
+
affected_metric.save unless opts[:dry_run]
|
|
921
|
+
end
|
|
922
|
+
|
|
923
|
+
# Then search which reports are still using this attribute after replacement in metric...
|
|
924
|
+
dependent = old_date.usedby
|
|
925
|
+
GoodData.logger.info 'Fixing reports (standard)...'
|
|
926
|
+
dependent.each do |dependent_object|
|
|
927
|
+
# This does not seem to work every time... some references are kept...
|
|
928
|
+
next if dependent_object['category'] != 'reportDefinition'
|
|
929
|
+
|
|
930
|
+
affected_rd = report_definitions(dependent_object['link'])
|
|
931
|
+
|
|
932
|
+
GoodData.logger.info "reportDefinition (#{affected_rd.uri}) contains old date attribute '#{old_date.title}' ...replacing"
|
|
933
|
+
affected_rd.replace(old_date.uri, new_date.uri)
|
|
934
|
+
|
|
935
|
+
# Affected_rd.replace(labels_mapping) #not sure if this is working correctly, try to do it one by one
|
|
936
|
+
labels_mapping.each_pair do |old_label, new_label|
|
|
937
|
+
affected_rd.replace(old_label.uri, new_label.uri)
|
|
938
|
+
end
|
|
939
|
+
|
|
940
|
+
affected_rd.save unless opts[:dry_run]
|
|
941
|
+
end
|
|
942
|
+
|
|
943
|
+
# Then search which dashboards and reports are still using this attribute after standard replacement in reports...
|
|
944
|
+
dependent = old_date.usedby
|
|
945
|
+
GoodData.logger.info 'Fixing reports (force) & dashboards...'
|
|
946
|
+
|
|
947
|
+
# If standard replace did not work, use force...
|
|
948
|
+
dependent.each do |dependent_object|
|
|
949
|
+
case dependent_object['category']
|
|
950
|
+
when 'reportDefinition'
|
|
951
|
+
affected_rd = report_definitions(dependent_object['link'])
|
|
952
|
+
|
|
953
|
+
GoodData.logger.info "reportDefinition '#{affected_rd.title}' (#{affected_rd.uri}) still contains old date attribute '#{old_date.title}' ...replacing by force"
|
|
954
|
+
json_replace.call(affected_rd, old_date.uri, new_date.uri)
|
|
955
|
+
|
|
956
|
+
# Iterate over all labels
|
|
957
|
+
labels_mapping.each_pair do |old_label, new_label|
|
|
958
|
+
json_replace.call(affected_rd, old_label.uri, new_label.uri)
|
|
959
|
+
end
|
|
960
|
+
|
|
961
|
+
affected_rd.save unless opts[:dry_run]
|
|
962
|
+
when 'projectDashboard'
|
|
963
|
+
affected_dashboard = dashboards(dependent_object['link'])
|
|
964
|
+
|
|
965
|
+
GoodData.logger.info "Dashboard '#{affected_dashboard.title}' (#{affected_dashboard.uri}) contains old date attribute '#{old_date.title}' ...replacing by force"
|
|
966
|
+
json_replace.call(affected_dashboard, old_date.uri, new_date.uri)
|
|
967
|
+
|
|
968
|
+
# Iterate over all labels
|
|
969
|
+
labels_mapping.each_pair do |old_label, new_label|
|
|
970
|
+
json_replace.call(affected_dashboard, old_label.uri, new_label.uri)
|
|
971
|
+
end
|
|
972
|
+
|
|
973
|
+
affected_dashboard.save unless opts[:dry_run]
|
|
974
|
+
end
|
|
975
|
+
end
|
|
976
|
+
|
|
977
|
+
mufs.each do |muf|
|
|
978
|
+
json_replace.call(muf, old_date.uri, new_date.uri)
|
|
979
|
+
end
|
|
980
|
+
end
|
|
981
|
+
end
|
|
982
|
+
|
|
792
983
|
# Helper for getting reports of a project
|
|
793
984
|
#
|
|
794
985
|
# @param [String | Number | Object] Anything that you can pass to GoodData::Report[id]
|
|
@@ -879,16 +1070,29 @@ module GoodData
|
|
|
879
1070
|
#
|
|
880
1071
|
# @param file File to be uploaded
|
|
881
1072
|
# @param schema Schema to be used
|
|
882
|
-
def upload(
|
|
883
|
-
|
|
1073
|
+
def upload(data, blueprint, dataset_name, options = {})
|
|
1074
|
+
GoodData::Model.upload_data(data, blueprint, dataset_name, options.merge(client: client, project: self))
|
|
884
1075
|
end
|
|
885
1076
|
|
|
886
1077
|
def uri
|
|
887
1078
|
data['links']['self'] if data && data['links'] && data['links']['self']
|
|
888
1079
|
end
|
|
889
1080
|
|
|
1081
|
+
# List of user filters within this project
|
|
1082
|
+
#
|
|
1083
|
+
# @return [Array<GoodData::MandatoryUserFilter>] List of mandatory user
|
|
1084
|
+
def user_filters
|
|
1085
|
+
url = "/gdc/md/#{pid}/userfilters"
|
|
1086
|
+
|
|
1087
|
+
tmp = client.get(url)
|
|
1088
|
+
tmp['userFilters']['items'].pmap do |filter|
|
|
1089
|
+
client.create(GoodData::MandatoryUserFilter, filter, project: self)
|
|
1090
|
+
end
|
|
1091
|
+
end
|
|
1092
|
+
|
|
890
1093
|
# List of users in project
|
|
891
1094
|
#
|
|
1095
|
+
#
|
|
892
1096
|
# @return [Array<GoodData::User>] List of users
|
|
893
1097
|
def users(opts = { offset: 0, limit: 1_000 })
|
|
894
1098
|
result = []
|
|
@@ -1087,10 +1291,10 @@ module GoodData
|
|
|
1087
1291
|
# metric_filter - Checks metadata for inconsistent metric filters.
|
|
1088
1292
|
# invalid_objects - Checks metadata for invalid/corrupted objects.
|
|
1089
1293
|
# asyncTask response
|
|
1090
|
-
def validate(filters = %w(ldm pdm metric_filter invalid_objects))
|
|
1294
|
+
def validate(filters = %w(ldm pdm metric_filter invalid_objects), options = {})
|
|
1091
1295
|
response = client.post "#{md['validate-project']}", 'validateProject' => filters
|
|
1092
1296
|
polling_link = response['asyncTask']['link']['poll']
|
|
1093
|
-
client.poll_on_response(polling_link) do |body|
|
|
1297
|
+
client.poll_on_response(polling_link, options) do |body|
|
|
1094
1298
|
body['wTaskStatus'] && body['wTaskStatus']['status'] == 'RUNNING'
|
|
1095
1299
|
end
|
|
1096
1300
|
end
|