gooddata 2.1.19 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. checksums.yaml +4 -4
  2. data/.gdc-ii-config.yaml +42 -1
  3. data/.github/workflows/build.yml +67 -0
  4. data/.github/workflows/pre-merge.yml +72 -0
  5. data/.pronto.yml +1 -0
  6. data/.rubocop.yml +2 -14
  7. data/CHANGELOG.md +47 -0
  8. data/Dockerfile +27 -14
  9. data/Dockerfile.jruby +5 -15
  10. data/Dockerfile.ruby +5 -7
  11. data/Gemfile +4 -2
  12. data/LICENSE +4409 -16
  13. data/README.md +6 -6
  14. data/Rakefile +1 -1
  15. data/SDK_VERSION +1 -1
  16. data/VERSION +1 -1
  17. data/bin/run_brick.rb +7 -0
  18. data/ci/mssql/pom.xml +62 -0
  19. data/ci/mysql/pom.xml +62 -0
  20. data/ci/redshift/pom.xml +4 -5
  21. data/docker-compose.lcm.yml +42 -4
  22. data/docker-compose.yml +42 -0
  23. data/gooddata.gemspec +21 -21
  24. data/k8s/charts/lcm-bricks/Chart.yaml +1 -1
  25. data/lcm.rake +11 -8
  26. data/lib/gooddata/bricks/base_pipeline.rb +26 -0
  27. data/lib/gooddata/bricks/brick.rb +0 -1
  28. data/lib/gooddata/bricks/middleware/aws_middleware.rb +35 -9
  29. data/lib/gooddata/bricks/middleware/execution_result_middleware.rb +3 -3
  30. data/lib/gooddata/bricks/pipeline.rb +2 -14
  31. data/lib/gooddata/cloud_resources/blobstorage/blobstorage_client.rb +98 -0
  32. data/lib/gooddata/cloud_resources/mssql/drivers/.gitkeepme +0 -0
  33. data/lib/gooddata/cloud_resources/mssql/mssql_client.rb +122 -0
  34. data/lib/gooddata/cloud_resources/mysql/drivers/.gitkeepme +0 -0
  35. data/lib/gooddata/cloud_resources/mysql/mysql_client.rb +121 -0
  36. data/lib/gooddata/cloud_resources/postgresql/postgresql_client.rb +0 -1
  37. data/lib/gooddata/cloud_resources/redshift/drivers/.gitkeepme +0 -0
  38. data/lib/gooddata/cloud_resources/redshift/redshift_client.rb +0 -2
  39. data/lib/gooddata/cloud_resources/snowflake/snowflake_client.rb +18 -1
  40. data/lib/gooddata/helpers/data_helper.rb +9 -4
  41. data/lib/gooddata/lcm/actions/base_action.rb +157 -0
  42. data/lib/gooddata/lcm/actions/collect_data_product.rb +2 -1
  43. data/lib/gooddata/lcm/actions/collect_meta.rb +3 -1
  44. data/lib/gooddata/lcm/actions/collect_projects_warning_status.rb +53 -0
  45. data/lib/gooddata/lcm/actions/collect_segment_clients.rb +14 -0
  46. data/lib/gooddata/lcm/actions/initialize_continue_on_error_option.rb +87 -0
  47. data/lib/gooddata/lcm/actions/migrate_gdc_date_dimension.rb +31 -4
  48. data/lib/gooddata/lcm/actions/provision_clients.rb +34 -5
  49. data/lib/gooddata/lcm/actions/synchronize_cas.rb +24 -4
  50. data/lib/gooddata/lcm/actions/synchronize_clients.rb +112 -11
  51. data/lib/gooddata/lcm/actions/synchronize_dataset_mappings.rb +89 -0
  52. data/lib/gooddata/lcm/actions/synchronize_etls_in_segment.rb +48 -11
  53. data/lib/gooddata/lcm/actions/synchronize_kd_dashboard_permission.rb +103 -0
  54. data/lib/gooddata/lcm/actions/synchronize_ldm.rb +79 -23
  55. data/lib/gooddata/lcm/actions/synchronize_ldm_layout.rb +98 -0
  56. data/lib/gooddata/lcm/actions/synchronize_pp_dashboard_permission.rb +108 -0
  57. data/lib/gooddata/lcm/actions/synchronize_schedules.rb +31 -1
  58. data/lib/gooddata/lcm/actions/synchronize_user_filters.rb +26 -18
  59. data/lib/gooddata/lcm/actions/synchronize_user_groups.rb +30 -4
  60. data/lib/gooddata/lcm/actions/synchronize_users.rb +11 -10
  61. data/lib/gooddata/lcm/actions/update_metric_formats.rb +202 -0
  62. data/lib/gooddata/lcm/data/delete_from_lcm_release.sql.erb +5 -0
  63. data/lib/gooddata/lcm/exceptions/lcm_execution_warning.rb +15 -0
  64. data/lib/gooddata/lcm/helpers/check_helper.rb +19 -0
  65. data/lib/gooddata/lcm/helpers/release_table_helper.rb +42 -8
  66. data/lib/gooddata/lcm/lcm2.rb +50 -4
  67. data/lib/gooddata/lcm/user_bricks_helper.rb +9 -0
  68. data/lib/gooddata/mixins/inspector.rb +1 -1
  69. data/lib/gooddata/mixins/md_object_query.rb +1 -0
  70. data/lib/gooddata/models/data_source.rb +5 -1
  71. data/lib/gooddata/models/dataset_mapping.rb +36 -0
  72. data/lib/gooddata/models/ldm_layout.rb +38 -0
  73. data/lib/gooddata/models/metadata/label.rb +26 -27
  74. data/lib/gooddata/models/project.rb +230 -30
  75. data/lib/gooddata/models/project_creator.rb +83 -6
  76. data/lib/gooddata/models/schedule.rb +13 -1
  77. data/lib/gooddata/models/segment.rb +2 -1
  78. data/lib/gooddata/models/user_filters/user_filter_builder.rb +162 -68
  79. data/lib/gooddata/rest/connection.rb +5 -3
  80. data/lib/gooddata/rest/phmap.rb +2 -0
  81. data/lib/gooddata.rb +1 -0
  82. data/lib/gooddata_brick_base.rb +35 -0
  83. data/sonar-project.properties +6 -0
  84. metadata +100 -68
  85. data/lib/gooddata/bricks/middleware/bulk_salesforce_middleware.rb +0 -37
  86. data/lib/gooddata/cloud_resources/redshift/drivers/log4j.properties +0 -15
@@ -19,6 +19,9 @@ using NilExtensions
19
19
 
20
20
  module GoodData
21
21
  module UserFilterBuilder
22
+ @all_domain_users = {}
23
+ @mutex = Mutex.new
24
+
22
25
  # Main Entry function. Gets values and processes them to get filters
23
26
  # that are suitable for other function to process.
24
27
  # Values can be read from file or provided inline as an array.
@@ -48,11 +51,26 @@ module GoodData
48
51
  end
49
52
 
50
53
  def self.read_file(file, options = {})
51
- memo = {}
52
- params = row_based?(options) ? { headers: false } : { headers: true }
54
+ memo = Hash[]
55
+ if row_based?(options)
56
+ read_data_without_header(file, memo, options)
57
+ else
58
+ read_data_with_header(file, memo, options)
59
+ end
60
+ memo
61
+ end
53
62
 
54
- CSV.foreach(file, params.merge(return_headers: false)) do |e|
55
- key, data = process_line(e, options)
63
+ def self.read_data_without_header(file, memo, options)
64
+ CSV.foreach(file, headers: false, return_headers: false) do |row|
65
+ key, data = process_line(row, options)
66
+ memo[key] = [] unless memo.key?(key)
67
+ memo[key].concat(data)
68
+ end
69
+ end
70
+
71
+ def self.read_data_with_header(file, memo, options)
72
+ CSV.foreach(file, headers: true, return_headers: false) do |row|
73
+ key, data = process_line(row, options)
56
74
  memo[key] = [] unless memo.key?(key)
57
75
  memo[key].concat(data)
58
76
  end
@@ -203,20 +221,30 @@ module GoodData
203
221
  # so it precaches the values and still be able to function for larger ones even
204
222
  # though that would mean tons of requests
205
223
  def self.get_small_labels(labels_cache)
206
- labels_cache.values.select { |label| label && label.values_count && label.values_count < 100_000 }
224
+ labels_cache.values.select { |label| label &.values_count &. < 100_000 }
207
225
  end
208
226
 
209
227
  # Creates a MAQL expression(s) based on the filter defintion.
210
228
  # Takes the filter definition looks up any necessary values and provides API executable MAQL
211
229
  # @param labels_cache e.g. { 'label_uri': label_object }
212
230
  # @param lookups_cache e.g. { 'label_uri': { "jirka@gooddata.com": 'value_uri' }}
213
- def self.create_expression(filter, labels_cache, lookups_cache, attr_cache, options = {})
231
+ # rubocop:disable Metrics/ParameterLists
232
+ def self.create_expression(filter, labels_cache, lookups_cache, attr_cache, options = {}, login)
214
233
  values = filter[:values]
215
234
  # Do not create MUF for label when all its values is NULL (https://jira.intgdc.com/browse/TMA-1361)
216
235
  non_null_values = values.select { |value| !value.nil? && value.downcase != 'null' }
217
236
  return ['TRUE', []] if non_null_values.empty?
218
237
 
219
238
  label = labels_cache[filter[:label]]
239
+ if label.nil?
240
+ err_message = "Unable to apply filter values: #{values} since the project: #{options[:project].pid} doesn't have label: #{filter[:label]} for login: #{login}"
241
+ if options[:ignore_missing_values]
242
+ GoodData.logger.warn(err_message)
243
+ return ['TRUE', []]
244
+ else
245
+ fail err_message
246
+ end
247
+ end
220
248
  errors = []
221
249
 
222
250
  element_uris_by_values = Hash[values.map do |v|
@@ -262,6 +290,7 @@ module GoodData
262
290
  end
263
291
  [expression, errors]
264
292
  end
293
+ # rubocop:enable Metrics/ParameterLists
265
294
 
266
295
  # Encapuslates the creation of filter
267
296
  def self.create_user_filter(expression, related)
@@ -273,6 +302,66 @@ module GoodData
273
302
  }
274
303
  end
275
304
 
305
+ def self.create_user_profile_mapping(filters, project_users, options = {})
306
+ domain = options[:domain]
307
+ found_list = {}
308
+ missing_list = []
309
+
310
+ # Get the list of user login from filters
311
+ login_list = filters.flat_map do |filter|
312
+ filter[:login]
313
+ end
314
+
315
+ # Then find user login in the users_brick_input
316
+ users_brick_input = options[:users_brick_input]
317
+ if users_brick_input&.any?
318
+ users_brick_input.map do |user|
319
+ login_list << user.with_indifferent_access['login']
320
+ end
321
+ end
322
+
323
+ login_list.uniq.flat_map do |login|
324
+ user = project_users.find { |u| u.login == login }
325
+ if user
326
+ found_list[login] = user.profile_url
327
+ else
328
+ missing_list << login
329
+ end
330
+ end
331
+ # rubocop:disable Metrics/BlockNesting
332
+ unless missing_list.empty? || domain.nil?
333
+ if missing_list.size < 100
334
+ missing_list.each do |login|
335
+ user = domain.find_user_by_login(login)
336
+ found_list[login] = user.links['self'] if user
337
+ end
338
+ else
339
+ if @all_domain_users[domain.name].nil?
340
+ @mutex.lock
341
+ if @all_domain_users[domain.name].nil?
342
+ domain_users = domain.users
343
+ @all_domain_users[domain.name] = domain_users
344
+ GoodData.logger.info("action=lcm_get_domain_users domain=#{domain.name} number_users=#{domain_users.size} number_missing_users=#{missing_list.size} use_cache=false")
345
+ else
346
+ domain_users = @all_domain_users[domain.name]
347
+ GoodData.logger.info("action=lcm_get_domain_users domain=##{domain.name} number_users=#{domain_users.size} number_missing_users=#{missing_list.size} use_cache=true")
348
+ end
349
+ @mutex.unlock
350
+ else
351
+ domain_users = @all_domain_users[domain.name]
352
+ GoodData.logger.info("action=lcm_get_domain_users domain=##{domain.name} number_users=#{domain_users.size} number_missing_users=#{missing_list.size} use_cache=true")
353
+ end
354
+
355
+ missing_list.each do |login|
356
+ user = domain_users.find { |u| u.login == login }
357
+ found_list[login] = user.links['self'] if user
358
+ end
359
+ end
360
+ end
361
+ # rubocop:enable Metrics/BlockNesting
362
+ found_list
363
+ end
364
+
276
365
  # Resolves and creates maql statements from filter definitions.
277
366
  # This method does not perform any modifications on API but
278
367
  # collects all the information that is needed to do so.
@@ -283,7 +372,7 @@ module GoodData
283
372
  #
284
373
  # @param filters [Array<Hash>] Filters definition
285
374
  # @return [Array] first is list of MAQL statements
286
- def self.maqlify_filters(filters, project_users, options = {})
375
+ def self.maqlify_filters(filters, user_profile_mapping, options = {})
287
376
  fail_early = options[:fail_early] == false ? false : true
288
377
  users_cache = options[:users_cache]
289
378
  labels_cache = create_label_cache(filters, options)
@@ -291,11 +380,10 @@ module GoodData
291
380
  lookups_cache = create_lookups_cache(small_labels)
292
381
  attrs_cache = create_attrs_cache(filters, options)
293
382
  create_filter_proc = proc do |login, f|
294
- expression, errors = create_expression(f, labels_cache, lookups_cache, attrs_cache, options)
383
+ expression, errors = create_expression(f, labels_cache, lookups_cache, attrs_cache, options, login)
295
384
  safe_login = login.downcase
296
385
  profiles_uri = if options[:type] == :muf
297
- project_user = project_users.find { |u| u.login == safe_login }
298
- project_user.nil? ? ('/gdc/account/profile/' + safe_login) : project_user.profile_url
386
+ user_profile_mapping[safe_login].nil? ? ('/gdc/account/profile/' + safe_login) : user_profile_mapping[safe_login]
299
387
  elsif options[:type] == :variable
300
388
  (users_cache[login] && users_cache[login].uri)
301
389
  else
@@ -393,7 +481,8 @@ module GoodData
393
481
 
394
482
  project_users = project.users
395
483
  filters = normalize_filters(user_filters)
396
- user_filters, errors = maqlify_filters(filters, project_users, options.merge(users_must_exist: users_must_exist, type: :muf))
484
+ user_profile_mapping = create_user_profile_mapping(filters, project_users, options)
485
+ user_filters, errors = maqlify_filters(filters, user_profile_mapping, options.merge(users_must_exist: users_must_exist, type: :muf))
397
486
  if !ignore_missing_values && !errors.empty?
398
487
  errors = errors.map do |e|
399
488
  e.merge(pid: project.pid)
@@ -404,7 +493,7 @@ module GoodData
404
493
  filters = user_filters.map { |data| client.create(MandatoryUserFilter, data, project: project) }
405
494
  to_create, to_delete = resolve_user_filters(filters, project.data_permissions)
406
495
 
407
- to_delete = sanitize_filters_to_delete(to_delete, options[:users_brick_input], project_users) unless options[:no_sanitize]
496
+ to_delete = sanitize_filters_to_delete(to_delete, options[:users_brick_input], user_profile_mapping) unless options[:no_sanitize]
408
497
 
409
498
  if options[:do_not_touch_filters_that_are_not_mentioned]
410
499
  GoodData.logger.warn("Data permissions computed: #{to_create.count} to create")
@@ -421,68 +510,73 @@ module GoodData
421
510
  results: create_results + delete_results }
422
511
  end
423
512
 
424
- create_results = to_create.each_slice(100).flat_map do |batch|
425
- batch.pmapcat do |related_uri, group|
426
- group.each(&:save)
427
- res = client.get("/gdc/md/#{project.pid}/userfilters?users=#{related_uri}")
428
- items = res['userFilters']['items'].empty? ? [] : res['userFilters']['items'].first['userFilters']
429
-
430
- payload = {
431
- 'userFilters' => {
432
- 'items' => [{
433
- 'user' => related_uri,
434
- 'userFilters' => items.concat(group.map(&:uri))
435
- }]
513
+ if to_create.empty?
514
+ create_results = []
515
+ else
516
+ create_results = to_create.each_slice(100).flat_map do |batch|
517
+ batch.pmapcat do |related_uri, group|
518
+ group.each(&:save)
519
+ res = client.get("/gdc/md/#{project.pid}/userfilters?users=#{related_uri}")
520
+ items = res['userFilters']['items'].empty? ? [] : res['userFilters']['items'].first['userFilters']
521
+
522
+ payload = {
523
+ 'userFilters' => {
524
+ 'items' => [{
525
+ 'user' => related_uri,
526
+ 'userFilters' => items.concat(group.map(&:uri))
527
+ }]
528
+ }
436
529
  }
437
- }
438
- res = client.post("/gdc/md/#{project.pid}/userfilters", payload)
530
+ res = client.post("/gdc/md/#{project.pid}/userfilters", payload)
439
531
 
440
- # turn the errors from hashes into array of hashes
441
- update_result = res['userFiltersUpdateResult'].flat_map do |k, v|
442
- v.map { |r| { status: k.to_sym, user: r, type: :create } }
443
- end
532
+ # turn the errors from hashes into array of hashes
533
+ update_result = res['userFiltersUpdateResult'].flat_map do |k, v|
534
+ v.map { |r| { status: k.to_sym, user: r, type: :create } }
535
+ end
444
536
 
445
- update_result.map do |result|
446
- result[:status] == :failed ? result.merge(GoodData::Helpers.symbolize_keys(result[:user])) : result
537
+ update_result.map do |result|
538
+ result[:status] == :failed ? result.merge(GoodData::Helpers.symbolize_keys(result[:user])) : result
539
+ end
447
540
  end
448
541
  end
542
+ project_log_formatter.log_user_filter_results(create_results, to_create)
543
+ create_errors = create_results.select { |r| r[:status] == :failed }
544
+ fail "Creating MUFs resulted in errors: #{create_errors}" if create_errors.any?
449
545
  end
450
546
 
451
- project_log_formatter.log_user_filter_results(create_results, to_create)
452
- create_errors = create_results.select { |r| r[:status] == :failed }
453
- fail "Creating MUFs resulted in errors: #{create_errors}" if create_errors.any?
454
-
455
- delete_results = unless options[:do_not_touch_filters_that_are_not_mentioned]
456
- to_delete.each_slice(100).flat_map do |batch|
457
- batch.flat_map do |related_uri, group|
458
- results = []
459
- if related_uri
460
- res = client.get("/gdc/md/#{project.pid}/userfilters?users=#{related_uri}")
461
- items = res['userFilters']['items'].empty? ? [] : res['userFilters']['items'].first['userFilters']
462
- payload = {
463
- 'userFilters' => {
464
- 'items' => [
465
- {
466
- 'user' => related_uri,
467
- 'userFilters' => items - group.map(&:uri)
468
- }
469
- ]
470
- }
471
- }
472
- res = client.post("/gdc/md/#{project.pid}/userfilters", payload)
473
- results.concat(res['userFiltersUpdateResult']
547
+ if to_delete.empty?
548
+ delete_results = []
549
+ elsif !options[:do_not_touch_filters_that_are_not_mentioned]
550
+ delete_results = to_delete.each_slice(100).flat_map do |batch|
551
+ batch.flat_map do |related_uri, group|
552
+ results = []
553
+ if related_uri
554
+ res = client.get("/gdc/md/#{project.pid}/userfilters?users=#{related_uri}")
555
+ items = res['userFilters']['items'].empty? ? [] : res['userFilters']['items'].first['userFilters']
556
+ payload = {
557
+ 'userFilters' => {
558
+ 'items' => [
559
+ {
560
+ 'user' => related_uri,
561
+ 'userFilters' => items - group.map(&:uri)
562
+ }
563
+ ]
564
+ }
565
+ }
566
+ res = client.post("/gdc/md/#{project.pid}/userfilters", payload)
567
+ results.concat(res['userFiltersUpdateResult']
474
568
  .flat_map { |k, v| v.map { |r| { status: k.to_sym, user: r, type: :delete } } }
475
569
  .map { |result| result[:status] == :failed ? result.merge(GoodData::Helpers.symbolize_keys(result[:user])) : result })
476
- end
477
- group.peach(&:delete)
478
- results
479
- end
480
- end
481
- end
570
+ end
571
+ group.peach(&:delete)
572
+ results
573
+ end
482
574
 
483
- project_log_formatter.log_user_filter_results(delete_results, to_delete)
484
- delete_errors = delete_results.select { |r| r[:status] == :failed } if delete_results
485
- fail "Deleting MUFs resulted in errors: #{delete_errors}" if delete_errors && delete_errors.any?
575
+ project_log_formatter.log_user_filter_results(delete_results, to_delete)
576
+ delete_errors = delete_results.select { |r| r[:status] == :failed } if delete_results
577
+ fail "Deleting MUFs resulted in errors: #{delete_errors}" if delete_errors&.any?
578
+ end
579
+ end
486
580
 
487
581
  { created: to_create, deleted: to_delete, results: create_results + (delete_results || []) }
488
582
  end
@@ -494,7 +588,7 @@ module GoodData
494
588
  # @param file [String | Array] File or array of values to be parsed for filters
495
589
  # @param options [Hash] Filter definitions
496
590
  # @return [Array<Hash>]
497
- def self.get_values(file, options)
591
+ def self.get_values(file, options = {})
498
592
  file.is_a?(Array) ? read_array(file, options) : read_file(file, options)
499
593
  end
500
594
 
@@ -579,12 +673,12 @@ module GoodData
579
673
  # Removes MUFs from to_delete unless in user is in users_brick_input
580
674
  # if this does not happen, users that are about to be deleted by users_brick
581
675
  # would have all their filters removed now, which is not desirable
582
- def self.sanitize_filters_to_delete(to_delete, users_brick_input, project_users)
676
+ def self.sanitize_filters_to_delete(to_delete, users_brick_input, user_profile_mapping)
583
677
  return [] unless users_brick_input && users_brick_input.any?
584
678
  user_profiles = users_brick_input.map do |user|
585
- result = project_users.find { |u| u.login == user.with_indifferent_access['login'] }
679
+ result = user_profile_mapping[user.with_indifferent_access['login']]
586
680
  next unless result
587
- result.profile_url
681
+ result
588
682
  end.compact
589
683
  return [] unless user_profiles.any?
590
684
  to_delete.reject do |_, value|
@@ -68,6 +68,8 @@ module GoodData
68
68
  RETRY_TIME_COEFFICIENT = 1.5
69
69
  RETRYABLE_ERRORS << Net::ReadTimeout if Net.const_defined?(:ReadTimeout)
70
70
 
71
+ RETRYABLE_ERRORS << OpenSSL::SSL::SSLErrorWaitReadable if OpenSSL::SSL.const_defined?(:SSLErrorWaitReadable)
72
+
71
73
  class << self
72
74
  def construct_login_payload(username, password)
73
75
  res = {
@@ -304,12 +306,12 @@ module GoodData
304
306
  end
305
307
 
306
308
  def refresh_token(_options = {})
307
- begin # rubocop:disable RedundantBegin
309
+ begin # rubocop:disable Style/RedundantBegin
308
310
  # avoid infinite loop GET fails with 401
309
311
  response = get(TOKEN_PATH, :x_gdc_authsst => sst_token, :dont_reauth => true)
310
312
  # Remove when TT sent in headers. Currently we need to parse from body
311
313
  merge_headers!(:x_gdc_authtt => GoodData::Helpers.get_path(response, %w(userToken token)))
312
- rescue Exception => e # rubocop:disable RescueException
314
+ rescue Exception => e # rubocop:disable Style/RescueException
313
315
  raise e
314
316
  end
315
317
  end
@@ -688,7 +690,7 @@ ERR
688
690
  execution_id = request_id
689
691
 
690
692
  GoodData.gd_logger.update_store(domain, method, duration, endpoint)
691
- GoodData.gd_logger.add Logger::INFO, { endpoint: endpoint, duration: duration, domain: domain,
693
+ GoodData.gd_logger.add Logger::DEBUG, { endpoint: endpoint, duration: duration, domain: domain,
692
694
  execution_id: execution_id, time_stamp: time_stamp }, "rest_call"
693
695
  end
694
696
  end
@@ -82,6 +82,8 @@ module GoodData
82
82
 
83
83
  ['/gdc/internal/projects/{id}/objects/setPermissions', %r{/gdc/internal/projects/[^\/]+/objects/setPermissions}],
84
84
  ['/gdc/internal/projects/{id}/roles', %r{/gdc/internal/projects/[^\/]+/roles}],
85
+ ['/gdc/dataload/projects/{id}/modelMapping/datasets/bulk/upsert', %r{/gdc/dataload/projects/[^\/]+/modelMapping/datasets/bulk/upsert}],
86
+ ['/gdc/dataload/internal/projects/{id}/ldmLayout', %r{/gdc/dataload/internal/projects/[^\/]+/ldmLayout}],
85
87
  ['/gdc/md/{id}/variables/item/{id}', %r{/gdc/md/[^\/]+/variables/item/[\d]+}],
86
88
  ['/gdc/md/{id}/validate/task/{id}', %r{/gdc/md/[^\/]+/validate/task/[^\/]+}],
87
89
  ['/gdc/md/{id}/using2/{id}/{id}', %r{/gdc/md/[^\/]+/using2/[\d]+/[\d]+}],
data/lib/gooddata.rb CHANGED
@@ -37,5 +37,6 @@ require 'backports/2.1.0/array/to_h'
37
37
 
38
38
  # Helpers
39
39
  require 'gooddata/helpers/global_helpers'
40
+ require 'gooddata/lcm/helpers/check_helper'
40
41
 
41
42
  require 'active_support/core_ext/hash/compact' unless RUBY_VERSION >= '2.5'
@@ -0,0 +1,35 @@
1
+ # encoding: UTF-8
2
+ # frozen_string_literal: true
3
+ #
4
+ # Copyright (c) 2010-2022 GoodData Corporation. All rights reserved.
5
+ # This source code is licensed under the BSD-style license found in the
6
+ # LICENSE file in the root directory of this source tree.
7
+
8
+ require 'pmap'
9
+ $pmap_default_thread_count = 20 # rubocop:disable GlobalVars
10
+
11
+ # GoodData Module
12
+ module GoodData
13
+ end
14
+
15
+ # Modules
16
+ require_relative 'gooddata/core/core'
17
+ require_relative 'gooddata/models/models'
18
+ require_relative 'gooddata/exceptions/exceptions'
19
+ require_relative 'gooddata/helpers/helpers'
20
+
21
+ # Files
22
+ require_relative 'gooddata/bricks/utils'
23
+ require_relative 'gooddata/bricks/brick'
24
+ require_relative 'gooddata/bricks/base_pipeline'
25
+ require_relative 'gooddata/bricks/middleware/base_middleware'
26
+ require_relative 'gooddata/bricks/middleware/bench_middleware'
27
+ require_relative 'gooddata/bricks/middleware/logger_middleware'
28
+ require_relative 'gooddata/bricks/middleware/decode_params_middleware'
29
+ require_relative 'gooddata/bricks/middleware/aws_middleware'
30
+ require_relative 'gooddata/bricks/middleware/dwh_middleware'
31
+ require_relative 'gooddata/bricks/middleware/bench_middleware'
32
+
33
+ # CSV Downloader
34
+ require_relative 'gooddata/core/logging'
35
+ require_relative 'gooddata/connection'
@@ -0,0 +1,6 @@
1
+ sonar.projectKey=gooddata-ruby
2
+ sonar.organization=gooddata-github
3
+ # Config scan necessary files/folder only
4
+ sonar.sources=bin,lib
5
+ sonar.tests=spec
6
+ sonar.exclusions=**/lib/templates/project/*.erb