gooddata 1.3.0-java → 1.3.1-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/.dockerignore +2 -0
  3. data/.document +0 -0
  4. data/.yardopts +0 -0
  5. data/CHANGELOG.md +48 -0
  6. data/CLI.md +0 -0
  7. data/CONTRIBUTING.md +19 -12
  8. data/Dockerfile +37 -0
  9. data/Guardfile +0 -0
  10. data/README.md +1 -1
  11. data/Rakefile +17 -1
  12. data/TODO.md +0 -0
  13. data/bin/run_brick.rb +31 -0
  14. data/dependency_decisions.yml +0 -0
  15. data/gooddata.gemspec +1 -0
  16. data/lib/gooddata/bricks/hello_world_brick.rb +21 -0
  17. data/lib/gooddata/bricks/middleware/gooddata_middleware.rb +12 -0
  18. data/lib/gooddata/bricks/middleware/logger_middleware.rb +12 -0
  19. data/lib/gooddata/bricks/pipeline.rb +12 -0
  20. data/lib/gooddata/exceptions/filter_maqlization.rb +0 -6
  21. data/lib/gooddata/extensions/class.rb +4 -0
  22. data/lib/gooddata/extensions/enumerable.rb +0 -3
  23. data/lib/gooddata/extensions/extensions.rb +0 -3
  24. data/lib/gooddata/extensions/false.rb +8 -16
  25. data/lib/gooddata/extensions/hash.rb +10 -41
  26. data/lib/gooddata/extensions/integer.rb +9 -3
  27. data/lib/gooddata/extensions/nil.rb +5 -13
  28. data/lib/gooddata/extensions/object.rb +0 -11
  29. data/lib/gooddata/extensions/string.rb +11 -5
  30. data/lib/gooddata/extensions/true.rb +8 -16
  31. data/lib/gooddata/helpers/global_helpers.rb +12 -0
  32. data/lib/gooddata/helpers/global_helpers_params.rb +5 -3
  33. data/lib/gooddata/lcm/actions/apply_custom_maql.rb +6 -0
  34. data/lib/gooddata/lcm/actions/base_action.rb +8 -2
  35. data/lib/gooddata/lcm/actions/collect_multiple_projects_column.rb +46 -0
  36. data/lib/gooddata/lcm/actions/collect_users_brick_users.rb +9 -2
  37. data/lib/gooddata/lcm/actions/execute_schedules.rb +0 -2
  38. data/lib/gooddata/lcm/actions/hello_world.rb +1 -1
  39. data/lib/gooddata/lcm/actions/synchronize_cas.rb +6 -0
  40. data/lib/gooddata/lcm/actions/synchronize_ldm.rb +6 -0
  41. data/lib/gooddata/lcm/actions/synchronize_user_filters.rb +70 -107
  42. data/lib/gooddata/lcm/actions/synchronize_users.rb +1 -13
  43. data/lib/gooddata/lcm/brick_logger.rb +26 -0
  44. data/lib/gooddata/lcm/lcm2.rb +46 -7
  45. data/lib/gooddata/lcm/types/base_type.rb +4 -0
  46. data/lib/gooddata/lcm/types/class/class.rb +2 -0
  47. data/lib/gooddata/lcm/types/complex/complex.rb +2 -0
  48. data/lib/gooddata/lcm/types/special/array.rb +2 -0
  49. data/lib/gooddata/mixins/is_folder.rb +0 -0
  50. data/lib/gooddata/mixins/to_json.rb +0 -0
  51. data/lib/gooddata/mixins/uri_getter.rb +0 -0
  52. data/lib/gooddata/models/blueprint/project_blueprint.rb +5 -5
  53. data/lib/gooddata/models/blueprint/to_manifest.rb +0 -2
  54. data/lib/gooddata/models/domain.rb +1 -0
  55. data/lib/gooddata/models/from_wire.rb +0 -2
  56. data/lib/gooddata/models/profile.rb +1 -1
  57. data/lib/gooddata/models/project.rb +5 -0
  58. data/lib/gooddata/models/user_filters/user_filter_builder.rb +49 -32
  59. data/lib/gooddata/models/user_group.rb +3 -0
  60. data/lib/gooddata/rest/README.md +0 -0
  61. data/lib/gooddata/rest/client.rb +4 -4
  62. data/lib/gooddata/rest/object.rb +2 -0
  63. data/lib/gooddata/version.rb +1 -1
  64. data/lib/templates/bricks/brick.rb.erb +0 -0
  65. data/lib/templates/bricks/main.rb.erb +0 -0
  66. data/lib/templates/project/Goodfile.erb +0 -0
  67. data/lib/templates/project/data/commits.csv +0 -0
  68. data/lib/templates/project/data/devs.csv +0 -0
  69. data/lib/templates/project/data/repos.csv +0 -0
  70. data/lib/templates/project/model/model.rb.erb +0 -0
  71. metadata +23 -5
  72. data/lib/gooddata/extensions/big_decimal.rb +0 -17
  73. data/lib/gooddata/extensions/numeric.rb +0 -15
  74. data/lib/gooddata/extensions/symbol.rb +0 -15
@@ -201,7 +201,6 @@ module GoodData
201
201
  new_users.group_by { |u| u[:pid] }.flat_map do |project_id, users|
202
202
  begin
203
203
  project = client.projects(project_id)
204
- fail "You (user executing the script - #{client.user.login}) is not admin in project \"#{project_id}\"." unless project.am_i_admin?
205
204
  project.import_users(users,
206
205
  domain: domain,
207
206
  whitelists: whitelists,
@@ -371,21 +370,10 @@ module GoodData
371
370
  country_column = params.country_column || 'country'
372
371
  phone_column = params.phone_column || 'phone'
373
372
  ip_whitelist_column = params.ip_whitelist_column || 'ip_whitelist'
374
- mode = params.sync_mode
375
373
 
376
374
  sso_provider = params.sso_provider
377
375
  authentication_modes = params.authentication_modes || []
378
376
 
379
- multiple_projects_column = params.multiple_projects_column
380
- unless multiple_projects_column
381
- client_modes = %w(sync_domain_client_workspaces sync_one_project_based_on_custom_id sync_multiple_projects_based_on_custom_id)
382
- multiple_projects_column = if client_modes.include?(mode)
383
- 'client_id'
384
- else
385
- 'project_id'
386
- end
387
- end
388
-
389
377
  dwh = params.ads_client
390
378
  if dwh
391
379
  data = dwh.execute_select(params.input_source.query)
@@ -423,7 +411,7 @@ module GoodData
423
411
  :sso_provider => sso_provider || row[sso_provider_column] || row[sso_provider_column.to_sym],
424
412
  :authentication_modes => modes,
425
413
  :user_group => user_group,
426
- :pid => multiple_projects_column.nil? ? nil : (row[multiple_projects_column] || row[multiple_projects_column.to_sym]),
414
+ :pid => params.multiple_projects_column.nil? ? nil : (row[params.multiple_projects_column] || row[params.multiple_projects_column.to_sym]),
427
415
  :language => row[language_column] || row[language_column.to_sym],
428
416
  :company => row[company_column] || row[company_column.to_sym],
429
417
  :position => row[position_column] || row[position_column.to_sym],
@@ -0,0 +1,26 @@
1
+ # encoding: UTF-8
2
+ #
3
+ # Copyright (c) 2010-2017 GoodData Corporation. All rights reserved.
4
+ # This source code is licensed under the BSD-style license found in the
5
+ # LICENSE file in the root directory of this source tree.
6
+
7
+ module GoodData
8
+ # Simple file logger.
9
+ class BrickFileLogger
10
+ # entry-point
11
+ # @param [String] log_directory directory to create log files
12
+ # @param [String] mode - brick mode (short name if brick)
13
+ def initialize(log_directory, mode)
14
+ @log_directory = log_directory
15
+ @mode = mode
16
+ end
17
+
18
+ # Creates file in log directory with given content. Logging is disabled when log_directory is nil.
19
+ #
20
+ # @param [String] status brick phase/status (start, finished, error,...)
21
+ # @param [String] content log file content
22
+ def log_action(status, content)
23
+ File.write("#{@log_directory}/#{@mode}_#{status}.json", content)
24
+ end
25
+ end
26
+ end
@@ -6,10 +6,26 @@
6
6
 
7
7
  require 'terminal-table'
8
8
 
9
+ require 'gooddata/extensions/class'
10
+ require 'gooddata/extensions/true'
11
+ require 'gooddata/extensions/false'
12
+ require 'gooddata/extensions/integer'
13
+ require 'gooddata/extensions/string'
14
+ require 'gooddata/extensions/nil'
15
+
16
+ require 'active_support/core_ext/hash/compact'
17
+
9
18
  require_relative 'actions/actions'
19
+ require_relative 'brick_logger'
10
20
  require_relative 'dsl/dsl'
11
21
  require_relative 'helpers/helpers'
12
22
 
23
+ using TrueExtensions
24
+ using FalseExtensions
25
+ using IntegerExtensions
26
+ using StringExtensions
27
+ using NilExtensions
28
+
13
29
  module GoodData
14
30
  module LCM2
15
31
  class SmartHash < Hash
@@ -139,12 +155,14 @@ module GoodData
139
155
 
140
156
  users: [
141
157
  CollectDataProduct,
158
+ CollectMultipleProjectsColumn,
142
159
  CollectSegments,
143
160
  SynchronizeUsers
144
161
  ],
145
162
 
146
163
  user_filters: [
147
164
  CollectDataProduct,
165
+ CollectMultipleProjectsColumn,
148
166
  CollectUsersBrickUsers,
149
167
  CollectSegments,
150
168
  SynchronizeUserFilters
@@ -152,6 +170,10 @@ module GoodData
152
170
 
153
171
  schedules_execution: [
154
172
  ExecuteSchedules
173
+ ],
174
+
175
+ hello_world: [
176
+ HelloWorld
155
177
  ]
156
178
  }
157
179
 
@@ -269,6 +291,16 @@ module GoodData
269
291
 
270
292
  # Get actions for mode specified
271
293
  actions = get_mode_actions(mode)
294
+
295
+ if params.key?('log_directory')
296
+ brick_logger = BrickFileLogger.new(params['log_directory'], mode)
297
+ logging_enabled = true
298
+ else
299
+ logging_enabled = false
300
+ end
301
+ if logging_enabled
302
+ brick_logger.log_action('start', JSON.pretty_generate(params))
303
+ end
272
304
  if params.actions
273
305
  actions = params.actions.map do |action|
274
306
  "GoodData::LCM2::#{action}".split('::').inject(Object) do |o, c|
@@ -283,8 +315,6 @@ module GoodData
283
315
 
284
316
  # TODO: Check all action params first
285
317
 
286
- new_params = params
287
-
288
318
  fail_early = if params.key?(:fail_early)
289
319
  params.fail_early.to_b
290
320
  else
@@ -321,6 +351,7 @@ module GoodData
321
351
  backtrace: e.backtrace
322
352
  }
323
353
  break if fail_early
354
+ results << nil
324
355
  end
325
356
 
326
357
  # in case fail_early = false, we need to execute another action
@@ -342,19 +373,27 @@ module GoodData
342
373
  results << res
343
374
  end
344
375
 
345
- # Fail whole execution if there is any failed action
346
- fail(JSON.pretty_generate(errors)) if strict_mode && errors.any?
347
-
348
376
  brick_results = {}
349
377
  actions.each_with_index do |action, index|
350
378
  brick_results[action.short_name] = results[index]
351
379
  end
352
380
 
353
- {
381
+ result = {
354
382
  actions: actions.map(&:short_name),
355
383
  results: brick_results,
356
- params: params
384
+ params: params,
385
+ success: errors.empty?
357
386
  }
387
+
388
+ # Fail whole execution if there is any failed action
389
+ fail(JSON.pretty_generate(errors)) if strict_mode && errors.any?
390
+
391
+ result
392
+
393
+ ensure
394
+ if logging_enabled
395
+ brick_logger.log_action('finished', JSON.pretty_generate(result))
396
+ end
358
397
  end
359
398
 
360
399
  def run_action(action, params)
@@ -8,6 +8,10 @@
8
8
  require_relative '../dsl/dsl'
9
9
  require_relative '../helpers/helpers'
10
10
 
11
+ require 'active_support/core_ext/hash/compact'
12
+
13
+ require 'gooddata/extensions/class'
14
+
11
15
  module GoodData
12
16
  module LCM2
13
17
  module Type
@@ -6,6 +6,8 @@
6
6
 
7
7
  require_relative '../base_type'
8
8
 
9
+ require 'gooddata/extensions/class'
10
+
9
11
  module GoodData
10
12
  module LCM2
11
13
  module Type
@@ -6,6 +6,8 @@
6
6
 
7
7
  require_relative '../base_type'
8
8
 
9
+ require 'gooddata/extensions/class'
10
+
9
11
  module GoodData
10
12
  module LCM2
11
13
  module Type
@@ -4,6 +4,8 @@
4
4
  # This source code is licensed under the BSD-style license found in the
5
5
  # LICENSE file in the root directory of this source tree.
6
6
 
7
+ require 'gooddata/extensions/class'
8
+
7
9
  module GoodData
8
10
  module LCM2
9
11
  module Type
File without changes
File without changes
File without changes
@@ -1,9 +1,9 @@
1
- # encoding: UTF-8
2
- #
3
1
  # Copyright (c) 2010-2017 GoodData Corporation. All rights reserved.
4
2
  # This source code is licensed under the BSD-style license found in the
5
3
  # LICENSE file in the root directory of this source tree.
6
4
 
5
+ require 'active_support/core_ext/hash/compact'
6
+
7
7
  module GoodData
8
8
  module Model
9
9
  class ProjectBlueprint
@@ -125,7 +125,7 @@ module GoodData
125
125
  def self.dataset?(project, name, options = {})
126
126
  find_dataset(project, name, options)
127
127
  true
128
- rescue
128
+ rescue StandardError
129
129
  false
130
130
  end
131
131
 
@@ -163,7 +163,7 @@ module GoodData
163
163
  def self.date_dimension?(project, name)
164
164
  find_date_dimension(project, name)
165
165
  true
166
- rescue
166
+ rescue StandardError
167
167
  false
168
168
  end
169
169
 
@@ -766,7 +766,7 @@ module GoodData
766
766
  errors.concat datasets.reduce([]) { |acc, elem| acc.concat(elem.validate) }
767
767
  errors.concat datasets.reduce([]) { |acc, elem| acc.concat(elem.validate_gd_data_type_errors) }
768
768
  errors
769
- rescue
769
+ rescue StandardError
770
770
  raise GoodData::ValidationError
771
771
  end
772
772
 
@@ -1,5 +1,3 @@
1
- # encoding: UTF-8
2
- #
3
1
  # Copyright (c) 2010-2017 GoodData Corporation. All rights reserved.
4
2
  # This source code is licensed under the BSD-style license found in the
5
3
  # LICENSE file in the root directory of this source tree.
@@ -5,6 +5,7 @@
5
5
  # LICENSE file in the root directory of this source tree.
6
6
 
7
7
  require 'cgi'
8
+ require 'active_support/core_ext/hash/compact'
8
9
 
9
10
  require_relative 'profile'
10
11
  require_relative '../extensions/enumerable'
@@ -1,5 +1,3 @@
1
- # encoding: UTF-8
2
- #
3
1
  # Copyright (c) 2010-2017 GoodData Corporation. All rights reserved.
4
2
  # This source code is licensed under the BSD-style license found in the
5
3
  # LICENSE file in the root directory of this source tree.
@@ -214,7 +214,7 @@ module GoodData
214
214
  #
215
215
  # @return [String] Email address
216
216
  def email
217
- @json['accountSetting']['email'] || ''
217
+ @json['accountSetting']['email'].is_a?(String) ? @json['accountSetting']['email'].downcase : ''
218
218
  end
219
219
 
220
220
  # Set the email
@@ -12,6 +12,10 @@ require 'pmap'
12
12
  require 'zip'
13
13
  require 'net/smtp'
14
14
 
15
+ require 'active_support/core_ext/hash/except'
16
+ require 'active_support/core_ext/hash/compact'
17
+ require 'active_support/core_ext/hash/slice'
18
+
15
19
  require_relative '../exceptions/no_project_error'
16
20
 
17
21
  require_relative '../helpers/auth_helpers'
@@ -1813,6 +1817,7 @@ module GoodData
1813
1817
  alias_method :create_users, :set_users_roles
1814
1818
 
1815
1819
  def add_data_permissions(filters, options = {})
1820
+ GoodData.logger.info("Synchronizing #{filters.count} filters in project #{pid}")
1816
1821
  GoodData::UserFilterBuilder.execute_mufs(filters, { client: client, project: self }.merge(options))
1817
1822
  end
1818
1823
 
@@ -5,7 +5,18 @@
5
5
  # LICENSE file in the root directory of this source tree.
6
6
 
7
7
  require_relative '../project_log_formatter'
8
+
8
9
  require 'active_support/core_ext/hash/indifferent_access'
10
+ require 'active_support/core_ext/hash/compact'
11
+
12
+ require 'gooddata/extensions/true'
13
+ require 'gooddata/extensions/false'
14
+ require 'gooddata/extensions/integer'
15
+
16
+ using FalseExtensions
17
+ using TrueExtensions
18
+ using IntegerExtensions
19
+ using NilExtensions
9
20
 
10
21
  module GoodData
11
22
  module UserFilterBuilder
@@ -199,47 +210,53 @@ module GoodData
199
210
  # Creates a MAQL expression(s) based on the filter defintion.
200
211
  # Takes the filter definition looks up any necessary values and provides API executable MAQL
201
212
  def self.create_expression(filter, labels_cache, lookups_cache, attr_cache, options = {})
202
- errors = []
203
213
  values = filter[:values]
204
214
  label = labels_cache[filter[:label]]
205
- element_uris = values.map do |v|
206
- begin
207
- if lookups_cache.key?(label.uri)
208
- if lookups_cache[label.uri].key?(v)
209
- lookups_cache[label.uri][v]
210
- else
211
- fail
212
- end
213
- else
214
- label.find_value_uri(v)
215
- end
216
- rescue
217
- errors << {
218
- type: :error,
219
- label: label.title,
220
- value: v
221
- }
222
- nil
215
+ errors = []
216
+
217
+ element_uris_by_values = Hash[values.map do |v|
218
+ if lookups_cache.key?(label.uri)
219
+ [v, lookups_cache[label.uri][v]]
220
+ else
221
+ [v, label.find_value_uri(v)]
223
222
  end
223
+ end]
224
+
225
+ missing_value_errors = element_uris_by_values.select { |_, v| v.nil? }.map do |k, _|
226
+ {
227
+ type: :error,
228
+ label: label.title,
229
+ value: k,
230
+ reason: 'Can not find the value of the attribute referenced in the MUF'
231
+ }
224
232
  end
225
- expression = if element_uris.compact.empty? && options[:restrict_if_missing_all_values] && options[:type] == :muf
226
- '1 <> 1'
227
- elsif element_uris.compact.empty? && options[:restrict_if_missing_all_values] && options[:type] == :variable
228
- nil
229
- elsif element_uris.compact.empty?
233
+ errors += missing_value_errors unless options[:ignore_missing_values]
234
+
235
+ element_uris = element_uris_by_values.values.compact
236
+ # happens when data is not yet loaded in the project
237
+ no_values = element_uris.empty?
238
+
239
+ expression = if no_values && options[:restrict_if_missing_all_values] && options[:type] == :muf
240
+ # create a filter that is always false to ensure the user can not see any data
241
+ # as the proper MUF can not be constructed yet
242
+ case options[:type]
243
+ when :muf
244
+ '1 <> 1'
245
+ when :variable
246
+ nil
247
+ end
248
+ elsif no_values
249
+ # create a filter that is always true to ensure the user can see all data
230
250
  'TRUE'
231
251
  elsif filter[:over] && filter[:to]
232
252
  over = attr_cache[filter[:over]]
233
253
  to = attr_cache[filter[:to]]
234
- "([#{label.attribute_uri}] IN (#{element_uris.compact.sort.map { |e| '[' + e + ']' }.join(', ')})) OVER [#{over && over.uri}] TO [#{to && to.uri}]"
254
+ "([#{label.attribute_uri}] IN (#{element_uris.sort.map { |e| '[' + e + ']' }.join(', ')})) OVER [#{over && over.uri}] TO [#{to && to.uri}]"
235
255
  else
236
- "[#{label.attribute_uri}] IN (#{element_uris.compact.sort.map { |e| '[' + e + ']' }.join(', ')})"
256
+ "[#{label.attribute_uri}] IN (#{element_uris.sort.map { |e| '[' + e + ']' }.join(', ')})"
237
257
  end
238
- if options[:ignore_missing_values]
239
- [expression, []]
240
- else
241
- [expression, errors]
242
- end
258
+
259
+ [expression, errors]
243
260
  end
244
261
 
245
262
  # Encapuslates the creation of filter
@@ -273,7 +290,7 @@ module GoodData
273
290
  expression, errors = create_expression(f, labels_cache, lookups_cache, attrs_cache, options)
274
291
  safe_login = login.downcase
275
292
  profiles_uri = if options[:type] == :muf
276
- project_user = project_users.find { |u| u.login == login }
293
+ project_user = project_users.find { |u| u.login == safe_login }
277
294
  project_user.nil? ? ('/gdc/account/profile/' + safe_login) : project_user.profile_url
278
295
  elsif options[:type] == :variable
279
296
  (users_cache[login] && users_cache[login].uri)
@@ -12,6 +12,9 @@ require_relative '../mixins/links'
12
12
  require_relative '../mixins/rest_resource'
13
13
  require_relative '../mixins/uri_getter'
14
14
 
15
+ require 'active_support/core_ext/hash/except'
16
+ require 'active_support/core_ext/hash/compact'
17
+
15
18
  module GoodData
16
19
  # Representation of User Group
17
20
  #
File without changes
@@ -314,27 +314,27 @@ module GoodData
314
314
  # Generalizaton of poller. Since we have quite a variation of how async proceses are handled
315
315
  # this is a helper that should help you with resources where the information about "Are we done"
316
316
  # is inside the response. It expects the URI as an input where it can poll and a block that should
317
- # return either true -> 'meaning we are done' or false -> meaning sleep and repeat. It returns the
317
+ # return either true (meaning sleep and repeat) or false (meaning we are done). It returns the
318
318
  # value of last poll. In majority of cases these are the data that you need
319
319
  #
320
320
  # @param link [String] Link for polling
321
321
  # @param options [Hash] Options
322
322
  # @return [Hash] Result of polling
323
323
  def poll_on_response(link, options = {}, &bl)
324
- sleep_interval = options[:sleep_interval] || DEFAULT_SLEEP_INTERVAL
325
324
  time_limit = options[:time_limit] || DEFAULT_POLL_TIME_LIMIT
326
325
  process = options[:process] == false ? false : true
327
326
 
328
327
  # get the first status and start the timer
329
328
  response = get(link, process: process)
330
329
  poll_start = Time.now
331
-
330
+ retry_time = GoodData::Rest::Connection::RETRY_TIME_INITIAL_VALUE
332
331
  while bl.call(response)
333
332
  limit_breached = time_limit && (Time.now - poll_start > time_limit)
334
333
  if limit_breached
335
334
  fail ExecutionLimitExceeded, "The time limit #{time_limit} secs for polling on #{link} is over"
336
335
  end
337
- sleep sleep_interval
336
+ sleep retry_time
337
+ retry_time *= GoodData::Rest::Connection::RETRY_TIME_COEFFICIENT
338
338
  GoodData::Rest::Client.retryable(:tries => Helpers::GD_MAX_RETRY, :refresh_token => proc { connection.refresh_token }) do
339
339
  response = get(link, process: process)
340
340
  end