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.
Files changed (133) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -1
  3. data/.travis.yml +8 -19
  4. data/Guardfile +5 -0
  5. data/README.md +1 -3
  6. data/bin/gooddata +1 -1
  7. data/gooddata.gemspec +6 -4
  8. data/lib/gooddata.rb +1 -1
  9. data/lib/gooddata/bricks/middleware/aws_middleware.rb +24 -0
  10. data/lib/gooddata/cli/commands/console_cmd.rb +1 -1
  11. data/lib/gooddata/cli/commands/project_cmd.rb +29 -9
  12. data/lib/gooddata/cli/hooks.rb +9 -3
  13. data/lib/gooddata/commands/datawarehouse.rb +1 -7
  14. data/lib/gooddata/commands/project.rb +4 -3
  15. data/lib/gooddata/core/logging.rb +14 -2
  16. data/lib/gooddata/exceptions/execution_limit_exceeded.rb +9 -0
  17. data/lib/gooddata/exceptions/uncomputable_report.rb +8 -0
  18. data/lib/gooddata/exceptions/validation_error.rb +1 -1
  19. data/lib/gooddata/goodzilla/goodzilla.rb +5 -1
  20. data/lib/gooddata/helpers/data_helper.rb +40 -9
  21. data/lib/gooddata/mixins/md_finders.rb +35 -0
  22. data/lib/gooddata/models/blueprint/anchor_field.rb +46 -0
  23. data/lib/gooddata/models/blueprint/attribute_field.rb +25 -0
  24. data/lib/gooddata/models/blueprint/blueprint.rb +7 -0
  25. data/lib/gooddata/models/blueprint/blueprint_field.rb +66 -0
  26. data/lib/gooddata/models/{dashboard_builder.rb → blueprint/dashboard_builder.rb} +0 -0
  27. data/lib/gooddata/models/{schema_blueprint.rb → blueprint/dataset_blueprint.rb} +176 -117
  28. data/lib/gooddata/models/blueprint/date_dimension.rb +10 -0
  29. data/lib/gooddata/models/blueprint/fact_field.rb +16 -0
  30. data/lib/gooddata/models/blueprint/label_field.rb +39 -0
  31. data/lib/gooddata/models/{project_blueprint.rb → blueprint/project_blueprint.rb} +366 -168
  32. data/lib/gooddata/models/blueprint/project_builder.rb +79 -0
  33. data/lib/gooddata/models/blueprint/reference_field.rb +39 -0
  34. data/lib/gooddata/models/blueprint/schema_blueprint.rb +156 -0
  35. data/lib/gooddata/models/blueprint/schema_builder.rb +85 -0
  36. data/lib/gooddata/models/{to_manifest.rb → blueprint/to_manifest.rb} +25 -20
  37. data/lib/gooddata/models/{to_wire.rb → blueprint/to_wire.rb} +33 -52
  38. data/lib/gooddata/models/datawarehouse.rb +2 -2
  39. data/lib/gooddata/models/domain.rb +3 -2
  40. data/lib/gooddata/models/execution.rb +2 -2
  41. data/lib/gooddata/models/execution_detail.rb +7 -2
  42. data/lib/gooddata/models/from_wire.rb +60 -71
  43. data/lib/gooddata/models/from_wire_parse.rb +125 -125
  44. data/lib/gooddata/models/metadata.rb +14 -0
  45. data/lib/gooddata/models/metadata/dashboard.rb +2 -2
  46. data/lib/gooddata/models/metadata/label.rb +1 -1
  47. data/lib/gooddata/models/metadata/report.rb +6 -5
  48. data/lib/gooddata/models/metadata/report_definition.rb +44 -59
  49. data/lib/gooddata/models/model.rb +131 -43
  50. data/lib/gooddata/models/process.rb +13 -11
  51. data/lib/gooddata/models/profile.rb +12 -1
  52. data/lib/gooddata/models/project.rb +223 -19
  53. data/lib/gooddata/models/project_creator.rb +4 -15
  54. data/lib/gooddata/models/schedule.rb +1 -0
  55. data/lib/gooddata/models/user_filters/user_filter_builder.rb +2 -2
  56. data/lib/gooddata/rest/client.rb +18 -18
  57. data/lib/gooddata/rest/connection.rb +113 -94
  58. data/lib/gooddata/version.rb +1 -1
  59. data/lib/templates/project/model/model.rb.erb +15 -16
  60. data/spec/data/blueprints/additional_dataset_module.json +32 -0
  61. data/spec/data/blueprints/big_blueprint_not_pruned.json +2079 -0
  62. data/spec/data/blueprints/invalid_blueprint.json +103 -0
  63. data/spec/data/blueprints/m_n_model.json +104 -0
  64. data/spec/data/blueprints/model_module.json +25 -0
  65. data/spec/data/blueprints/test_blueprint.json +38 -0
  66. data/spec/data/blueprints/test_project_model_spec.json +106 -0
  67. data/spec/data/gd_gse_data_manifest.json +34 -34
  68. data/spec/data/manifests/test_blueprint.json +32 -0
  69. data/spec/data/{manifest_test_project.json → manifests/test_project.json} +9 -18
  70. data/spec/data/wire_models/test_blueprint.json +63 -0
  71. data/spec/data/wire_test_project.json +5 -5
  72. data/spec/environment/default.rb +33 -0
  73. data/spec/environment/develop.rb +26 -0
  74. data/spec/environment/environment.rb +14 -0
  75. data/spec/environment/hotfix.rb +17 -0
  76. data/spec/environment/production.rb +31 -0
  77. data/spec/environment/release.rb +17 -0
  78. data/spec/helpers/blueprint_helper.rb +10 -7
  79. data/spec/helpers/cli_helper.rb +24 -22
  80. data/spec/helpers/connection_helper.rb +27 -25
  81. data/spec/helpers/crypto_helper.rb +7 -5
  82. data/spec/helpers/csv_helper.rb +5 -3
  83. data/spec/helpers/process_helper.rb +15 -10
  84. data/spec/helpers/project_helper.rb +40 -33
  85. data/spec/helpers/schedule_helper.rb +15 -9
  86. data/spec/helpers/spec_helper.rb +11 -0
  87. data/spec/integration/blueprint_updates_spec.rb +93 -0
  88. data/spec/integration/command_datawarehouse_spec.rb +2 -1
  89. data/spec/integration/command_projects_spec.rb +9 -8
  90. data/spec/integration/create_from_template_spec.rb +1 -1
  91. data/spec/integration/create_project_spec.rb +1 -1
  92. data/spec/integration/full_process_schedule_spec.rb +1 -1
  93. data/spec/integration/full_project_spec.rb +91 -30
  94. data/spec/integration/over_to_user_filters_spec.rb +24 -28
  95. data/spec/integration/partial_md_export_import_spec.rb +4 -4
  96. data/spec/integration/project_spec.rb +1 -1
  97. data/spec/integration/rest_spec.rb +1 -1
  98. data/spec/integration/user_filters_spec.rb +19 -24
  99. data/spec/integration/variables_spec.rb +7 -9
  100. data/spec/logging_in_logging_out_spec.rb +1 -1
  101. data/spec/spec_helper.rb +10 -1
  102. data/spec/unit/bricks/middleware/aws_middelware_spec.rb +47 -0
  103. data/spec/unit/core/connection_spec.rb +2 -2
  104. data/spec/unit/core/logging_spec.rb +12 -4
  105. data/spec/unit/helpers/data_helper_spec.rb +60 -0
  106. data/spec/unit/models/blueprint/attributes_spec.rb +24 -0
  107. data/spec/unit/models/blueprint/dataset_spec.rb +116 -0
  108. data/spec/unit/models/blueprint/labels_spec.rb +39 -0
  109. data/spec/unit/models/blueprint/project_blueprint_spec.rb +643 -0
  110. data/spec/unit/models/blueprint/reference_spec.rb +24 -0
  111. data/spec/unit/models/{schema_builder_spec.rb → blueprint/schema_builder_spec.rb} +12 -4
  112. data/spec/unit/models/blueprint/to_wire_spec.rb +169 -0
  113. data/spec/unit/models/domain_spec.rb +13 -2
  114. data/spec/unit/models/from_wire_spec.rb +277 -98
  115. data/spec/unit/models/metadata_spec.rb +22 -4
  116. data/spec/unit/models/model_spec.rb +49 -39
  117. data/spec/unit/models/profile_spec.rb +1 -0
  118. data/spec/unit/models/project_spec.rb +7 -7
  119. data/spec/unit/models/schedule_spec.rb +20 -0
  120. data/spec/unit/models/to_manifest_spec.rb +31 -11
  121. data/spec/unit/rest/polling_spec.rb +86 -0
  122. metadata +102 -30
  123. data/lib/gooddata/models/project_builder.rb +0 -136
  124. data/lib/gooddata/models/schema_builder.rb +0 -77
  125. data/out.txt +0 -0
  126. data/spec/data/additional_dataset_module.json +0 -18
  127. data/spec/data/blueprint_invalid.json +0 -38
  128. data/spec/data/m_n_model/blueprint.json +0 -76
  129. data/spec/data/model_module.json +0 -18
  130. data/spec/data/test_project_model_spec.json +0 -76
  131. data/spec/unit/models/attribute_column_spec.rb +0 -7
  132. data/spec/unit/models/project_blueprint_spec.rb +0 -239
  133. 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 zip_and_upload(path, files_to_exclude, opts = {})
146
- def with_zip(opts = {})
147
- Tempfile.open('deploy-graph-archive') do |temp|
148
- zip_filename = temp.path
149
- File.open(zip_filename, 'w') do |zip|
150
- Zip::File.open(zip.path, Zip::File::CREATE) do |zipfile|
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 # rubocop:disable Style/GuardClause
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
- d['project']['content']['authorizationToken'] = data[:auth_token] if data[:auth_token]
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, token: options[:auth_token], client: GoodData.connection))
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(file, dataset_blueprint, mode = 'FULL')
883
- dataset_blueprint.upload file, self, mode
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