gooddata 0.6.18 → 0.6.19

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