gooddata 0.6.18 → 0.6.19
Sign up to get free protection for your applications and to get access to all the features.
- 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
|