gooddata 0.6.50 → 0.6.51

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/.editorconfig +12 -0
  3. data/.gitignore +1 -0
  4. data/CHANGELOG.md +3 -0
  5. data/gooddata.gemspec +2 -1
  6. data/lib/gooddata/bricks/middleware/aws_middleware.rb +4 -0
  7. data/lib/gooddata/bricks/middleware/decode_params_middleware.rb +1 -1
  8. data/lib/gooddata/bricks/middleware/dwh_middleware.rb +1 -0
  9. data/lib/gooddata/bricks/middleware/gooddata_middleware.rb +1 -0
  10. data/lib/gooddata/bricks/middleware/logger_middleware.rb +2 -1
  11. data/lib/gooddata/core/nil_logger.rb +9 -0
  12. data/lib/gooddata/goodzilla/goodzilla.rb +1 -1
  13. data/lib/gooddata/helpers/data_helper.rb +1 -0
  14. data/lib/gooddata/helpers/global_helpers_params.rb +54 -27
  15. data/lib/gooddata/lcm/actions/apply_custom_maql.rb +70 -0
  16. data/lib/gooddata/lcm/actions/associate_clients.rb +17 -4
  17. data/lib/gooddata/lcm/actions/collect_clients.rb +4 -1
  18. data/lib/gooddata/lcm/actions/collect_segment_clients.rb +1 -0
  19. data/lib/gooddata/lcm/actions/collect_segments.rb +15 -2
  20. data/lib/gooddata/lcm/actions/create_segment_masters.rb +2 -2
  21. data/lib/gooddata/lcm/actions/provision_clients.rb +2 -4
  22. data/lib/gooddata/lcm/actions/purge_clients.rb +2 -2
  23. data/lib/gooddata/lcm/actions/synchronize_etls_in_segment.rb +81 -0
  24. data/lib/gooddata/lcm/actions/synchronize_label_types.rb +2 -2
  25. data/lib/gooddata/lcm/actions/synchronize_processes.rb +3 -1
  26. data/lib/gooddata/lcm/data/create_lcm_release.sql.erb +2 -1
  27. data/lib/gooddata/lcm/helpers/check_helper.rb +1 -1
  28. data/lib/gooddata/lcm/lcm.rb +29 -11
  29. data/lib/gooddata/lcm/lcm2.rb +82 -20
  30. data/lib/gooddata/models/domain.rb +22 -1
  31. data/lib/gooddata/models/metadata.rb +13 -8
  32. data/lib/gooddata/models/metadata/attribute.rb +1 -1
  33. data/lib/gooddata/models/metadata/report_definition.rb +1 -0
  34. data/lib/gooddata/models/profile.rb +1 -1
  35. data/lib/gooddata/models/project.rb +162 -38
  36. data/lib/gooddata/models/project_creator.rb +26 -6
  37. data/lib/gooddata/models/project_log_formatter.rb +204 -0
  38. data/lib/gooddata/models/schedule.rb +2 -21
  39. data/lib/gooddata/models/segment.rb +26 -0
  40. data/lib/gooddata/models/style_setting.rb +5 -1
  41. data/lib/gooddata/models/user_filters/user_filter_builder.rb +9 -0
  42. data/lib/gooddata/rest/connection.rb +4 -1
  43. data/lib/gooddata/version.rb +1 -1
  44. data/spec/environment/development.rb +29 -0
  45. data/spec/environment/environment.rb +14 -2
  46. data/spec/environment/{hotfix.rb → testing.rb} +0 -0
  47. data/spec/integration/date_dim_switch_spec.rb +3 -5
  48. data/spec/integration/lcm_spec.rb +24 -21
  49. data/spec/integration/project_spec.rb +16 -0
  50. data/spec/integration/segments_spec.rb +1 -1
  51. data/spec/unit/helpers/global_helpers_spec.rb +26 -2
  52. data/spec/unit/helpers_spec.rb +20 -0
  53. data/spec/unit/models/project_creator_spec.rb +3 -2
  54. metadata +29 -12
  55. data/lib/gooddata/lcm/actions/ensure_titles.rb +0 -54
  56. data/spec/environment/develop.rb +0 -46
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ba2ae6d7826f6eb42b8f24eb7774313adebfcda7
4
- data.tar.gz: e7589c5893af0dcc78fcb16f8b9bd1a8562b2f00
3
+ metadata.gz: d1715a4ba9d7203654418b0a275dd7eaf7e5e1dc
4
+ data.tar.gz: 42547b8e7ccbef65b3a0c6b0cb276886c55adcd8
5
5
  SHA512:
6
- metadata.gz: 7910edb7b3f5e2dc18f669034e983e5dee218dc9cf193017cfaf3f23b0a4fe9ddf3b3e5a4efdd1913aa95294e06edf30e1ee5feb38274deb29efc49471388484
7
- data.tar.gz: eb863dfe3ead470750f8b0fab9b402989c07a51aeaadfeefa08219a7975f9795a60d58dd441789112ee51263cb3db24539ca626442612267f8cc543da9bb412f
6
+ metadata.gz: d8120afa7ca7037fbd372960fe2ad02e9d2dc92d2d9dd17ce614b3f8d2a9d4e12412fe6ee8c3c2bd9f1c67ffdbf7962c099f612ec10a625188e2dfe0b5f8f2c8
7
+ data.tar.gz: a8485d9b645c943c91386111a4f4b6d12c66162d141bb747e4865761d2ca15ebdad92900d8ed6f3596ebdd55048af6c1ad3cf651049ccf04fb0e7b9edc2acfad
data/.editorconfig ADDED
@@ -0,0 +1,12 @@
1
+ root = true
2
+
3
+ [*]
4
+ indent_style = space
5
+ indent_size = 2
6
+ end_of_line = lf
7
+ charset = utf-8
8
+ trim_trailing_whitespace = true
9
+ insert_final_newline = true
10
+
11
+ [*.{sh,markdown}]
12
+ indent_size = 4
data/.gitignore CHANGED
@@ -12,6 +12,7 @@ tmtags
12
12
 
13
13
  ## VIM
14
14
  *.swp
15
+ .tags*
15
16
 
16
17
  ## IDEA
17
18
  .idea/
data/CHANGELOG.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # GoodData Ruby SDK Changelog
2
2
 
3
+ ## 0.6.52
4
+
3
5
  ## 0.6.50
4
6
  - Add support for Email Notification Rules on Process
5
7
  - Add support for exclude_schedules option in Project#export_clone
@@ -10,6 +12,7 @@
10
12
  - Support for custom color palette
11
13
  - Support for trasfering attribute drill paths
12
14
  - Implemented basic version of LCM2
15
+ - Add more logging for user, user filter management
13
16
 
14
17
  ## 0.6.49
15
18
  - Implement Helpers::GD_MAX_RETRY to allow max retries override
data/gooddata.gemspec CHANGED
@@ -34,12 +34,13 @@ Gem::Specification.new do |s|
34
34
  s.add_development_dependency 'redcarpet', '~> 3.1' if RUBY_PLATFORM != 'java'
35
35
  s.add_development_dependency 'rspec', '~> 3.5'
36
36
  s.add_development_dependency 'rspec-expectations', '~> 3.5'
37
- s.add_development_dependency 'rubocop', '~> 0.47'
37
+ s.add_development_dependency 'rubocop', '< 0.48' # TODO: remove it after dealing with new rules introduced in 0.48 version
38
38
  s.add_development_dependency 'simplecov', '~> 0.12'
39
39
  s.add_development_dependency 'webmock', '~> 1.21'
40
40
  s.add_development_dependency 'yard', '~> 0.8'
41
41
  s.add_development_dependency 'yard-rspec', '~> 0.1'
42
42
  s.add_development_dependency 'ZenTest', '~> 4.11'
43
+ s.add_development_dependency 'pry'
43
44
 
44
45
  s.add_dependency 'aws-sdk', '~> 2.7'
45
46
  s.add_dependency 'nokogiri', '~> 1.6.8'
@@ -17,6 +17,10 @@ module GoodData
17
17
  raise 'Unable to connect to AWS. Parameter "aws_client" seems to be empty' unless params['aws_client']
18
18
  raise 'Unable to connect to AWS. Parameter "access_key_id" is missing' if params['aws_client']['access_key_id'].blank?
19
19
  raise 'Unable to connect to AWS. Parameter "secret_access_key" is missing' if params['aws_client']['secret_access_key'].blank?
20
+ if params['aws_client'].key?('use_ssl')
21
+ params['aws_client']['use_ssl'] =
22
+ params['aws_client']['use_ssl'].to_b
23
+ end
20
24
  s3 = AWS::S3.new(params['aws_client'])
21
25
  params['aws_client']['s3_client'] = s3
22
26
  end
@@ -14,7 +14,7 @@ module GoodData
14
14
  params = params.to_hash
15
15
 
16
16
  # Decode and resolve reference parameters in gd_encoded_params
17
- @app.call(GoodData::Helpers.decode_params(params, :resolve_reference_params => true))
17
+ @app.call(GoodData::Helpers.decode_params(params, resolve_reference_params: true, convert_pipe_delimited_params: true))
18
18
  end
19
19
  end
20
20
  end
@@ -10,6 +10,7 @@ require 'gooddata_datawarehouse' if RUBY_PLATFORM == 'java'
10
10
 
11
11
  module GoodData
12
12
  module Bricks
13
+ # Connects to the warehouse (ADS) and enriches parameters with GoodData::Datawarehouse
13
14
  class WarehouseMiddleware < Bricks::Middleware
14
15
  def call(params)
15
16
  if params.key?('ads_client') && (RUBY_PLATFORM == 'java')
@@ -8,6 +8,7 @@ require_relative 'base_middleware'
8
8
 
9
9
  module GoodData
10
10
  module Bricks
11
+ # Connects to platform and enriches parameters with GoodData::Client
11
12
  class GoodDataMiddleware < Bricks::Middleware
12
13
  DEFAULT_PROTOCOL = 'https'
13
14
  DEFAULT_HOSTNAME = 'secure.gooddata.com'
@@ -17,9 +17,10 @@ module GoodData
17
17
  if params['GDC_LOGGING_OFF']
18
18
  logger = NilLogger.new
19
19
  else
20
- logger = params['GDC_LOGGER'] = params[:GDC_LOGGER_FILE].nil? ? Logger.new(STDOUT) : Logger.new(params[:GDC_LOGGER_FILE])
20
+ logger = params[:GDC_LOGGER_FILE].nil? ? Logger.new(STDOUT) : Logger.new(params[:GDC_LOGGER_FILE])
21
21
  logger.info('Pipeline starts')
22
22
  end
23
+ params['GDC_LOGGER'] = logger
23
24
  returning(@app.call(params)) do |_result|
24
25
  logger.info('Pipeline ending')
25
26
  end
@@ -19,5 +19,14 @@ module GoodData
19
19
  alias_method :info, :debug
20
20
  alias_method :warn, :debug
21
21
  alias_method :error, :debug
22
+
23
+ def debug?
24
+ false
25
+ end
26
+
27
+ alias_method :info?, :debug?
28
+ alias_method :warn?, :debug?
29
+ alias_method :error?, :debug?
30
+ alias_method :fatal?, :debug?
22
31
  end
23
32
  end
@@ -11,7 +11,7 @@ module GoodData
11
11
  # @param maql Input MAQL string
12
12
  # @return [Array<Array>] Pairs [attribute, attribute_element]
13
13
  def extract_element_uri_pairs(maql)
14
- arr = maql.scan(%r{\[([^\[\]]*)\/elements\?id=(\d+)\]}).flatten
14
+ arr = maql.scan(%r{(\/gdc\/(?:projects|md)\/[a-zA-Z\d]+\/obj\/\d+)\/elements\?id=(\d+)}).flatten
15
15
  evens = arr.select.each_with_index { |_, i| i.even? }
16
16
  odds = arr.select.each_with_index { |_, i| i.odd? }.map(&:to_i)
17
17
  evens.zip(odds)
@@ -102,6 +102,7 @@ module GoodData
102
102
  end
103
103
 
104
104
  def realize_s3(params)
105
+ params = GoodData::Helpers.stringify_keys(params)
105
106
  s3_client = params['aws_client'] && params['aws_client']['s3_client']
106
107
  raise 'AWS client not present. Perhaps S3Middleware is missing in the brick definition?' if !s3_client || !s3_client.respond_to?(:buckets)
107
108
  bucket_name = @options[:bucket]
@@ -57,26 +57,6 @@ module GoodData
57
57
  # @option options [Boolean] :resolve_reference_params Resolve reference parameters in gd_encoded_params or not
58
58
  # @return [Hash] Decoded parameters
59
59
  def decode_params(params, options = {})
60
- convert_secure_params = lambda do |args|
61
- args = args.select { |k, _| k.include? "|" }
62
- lines = args.keys.map do |key|
63
- hash = {}
64
- last_a = nil
65
- last_e = nil
66
- key.split("|").reduce(hash) do |a, e|
67
- last_a = a
68
- last_e = e
69
- a[e] = {}
70
- end
71
- last_a[last_e] = args[key]
72
- hash
73
- end
74
-
75
- lines.reduce({}) do |a, e|
76
- a.deep_merge(e)
77
- end
78
- end
79
-
80
60
  key = ENCODED_PARAMS_KEY.to_s
81
61
  hidden_key = ENCODED_HIDDEN_PARAMS_KEY.to_s
82
62
  data_params = params[key] || '{}'
@@ -103,6 +83,8 @@ module GoodData
103
83
  v.map do |v2|
104
84
  resolve_reference.call(v2)
105
85
  end
86
+ elsif !v.is_a?(String)
87
+ v
106
88
  else
107
89
  v.gsub(regexps) do |match|
108
90
  if match =~ /\\\\/
@@ -130,21 +112,47 @@ module GoodData
130
112
  begin
131
113
  parsed_data_params = data_params.is_a?(Hash) ? data_params : JSON.parse(data_params)
132
114
  parsed_hidden_data_params = hidden_data_params.is_a?(Hash) ? hidden_data_params : JSON.parse(hidden_data_params)
133
- rescue JSON::ParserError => e
134
- raise e.class, "Error reading json from '#{key}' or '#{hidden_key}', reason: #{e.message}"
115
+ rescue JSON::ParserError => exception
116
+ raise exception.class, "Error reading json from '#{key}' or '#{hidden_key}', reason: #{exception.message}"
135
117
  end
136
118
 
137
119
  # Add the nil on ENCODED_HIDDEN_PARAMS_KEY
138
120
  # if the data was retrieved from API You will not have the actual values so encode -> decode is not losless. The nil on the key prevents the server from deleting the key
139
121
  parsed_hidden_data_params[ENCODED_HIDDEN_PARAMS_KEY] = nil unless parsed_hidden_data_params.empty?
140
- secure_params = convert_secure_params.call(params)
141
- params.delete_if do |k, _|
142
- k.include?('|')
143
- end
144
122
 
145
123
  params.delete(key)
146
124
  params.delete(hidden_key)
147
- params.deep_merge(parsed_data_params).deep_merge(parsed_hidden_data_params).deep_merge(secure_params)
125
+ params = params.deep_merge(parsed_data_params).deep_merge(parsed_hidden_data_params)
126
+
127
+ if options[:convert_pipe_delimited_params]
128
+ convert_pipe_delimited_params = lambda do |args|
129
+ args = args.select { |k, _| k.include? "|" }
130
+ lines = args.keys.map do |k|
131
+ hash = {}
132
+ last_a = nil
133
+ last_e = nil
134
+ k.split("|").reduce(hash) do |a, e|
135
+ last_a = a
136
+ last_e = e
137
+ a[e] = {}
138
+ end
139
+ last_a[last_e] = args[k]
140
+ hash
141
+ end
142
+
143
+ lines.reduce({}) do |a, e|
144
+ a.deep_merge(e)
145
+ end
146
+ end
147
+
148
+ pipe_delimited_params = convert_pipe_delimited_params.call(params)
149
+ params.delete_if do |k, _|
150
+ k.include?('|')
151
+ end
152
+ params = params.deep_merge(pipe_delimited_params)
153
+ end
154
+
155
+ params
148
156
  end
149
157
 
150
158
  # A helper which allows you to diff two lists of objects. The objects
@@ -233,6 +241,25 @@ module GoodData
233
241
  end
234
242
  lookup
235
243
  end
244
+
245
+ def stringify_values(value)
246
+ case value
247
+ when nil
248
+ value
249
+ when Hash
250
+ Hash[
251
+ value.map do |k, v|
252
+ [k, stringify_values(v)]
253
+ end
254
+ ]
255
+ when Array
256
+ value.map do |v|
257
+ stringify_values(v)
258
+ end
259
+ else
260
+ value.to_s
261
+ end
262
+ end
236
263
  end
237
264
  end
238
265
  end
@@ -0,0 +1,70 @@
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
+ require_relative 'base_action'
8
+
9
+ module GoodData
10
+ module LCM2
11
+ # Applies custom MAQL DDL to all client projects so customized
12
+ # labels and fiscal calendars are not deleted.
13
+ # To ease up further automation, the MAQL DDL may be
14
+ # stored in separate field in lcm_release
15
+ # table as we will need custom Release brick action which will populate it.
16
+ class ApplyCustomMaql < BaseAction
17
+ DESCRIPTION = 'Apply Custom MAQL DDL'
18
+
19
+ PARAMS = define_params(self) do
20
+ description 'Should be custom MAQL DDL Applied'
21
+ param :apply_maql_ddl, instance_of(Type::BooleanType), required: false, default: false
22
+ end
23
+
24
+ RESULT_HEADER = [
25
+ :segment,
26
+ :maql,
27
+ :status
28
+ ]
29
+
30
+ class << self
31
+ def call(params)
32
+ return [] unless params.apply_maql_ddl.to_b
33
+
34
+ client = params.gdc_gd_client
35
+
36
+ domain_name = params.organization || params.domain
37
+ domain = client.domain(domain_name) || fail("Invalid domain name specified - #{domain_name}")
38
+
39
+ segment_ids = params.segments.map(&:segment_id)
40
+ domain_segments = domain.segments.select do |ds|
41
+ segment_ids.include?(ds.segment_id)
42
+ end
43
+
44
+ res = []
45
+ domain_segments.peach do |ds|
46
+ maql = 'CREATE DATASET {dataset.quotes} VISUAL (TITLE "Stock Quotes Data");'
47
+
48
+ unless maql.empty?
49
+ ds.clients.peach do |dc|
50
+ project = dc.project
51
+
52
+ r = project.execute_maql(maql)
53
+
54
+ item = {
55
+ segment: ds.segment_id,
56
+ maql: maql,
57
+ status: r
58
+ }
59
+
60
+ res.push(item)
61
+ end
62
+ end
63
+ end
64
+
65
+ res
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -31,14 +31,27 @@ module GoodData
31
31
 
32
32
  client = params.gdc_gd_client
33
33
 
34
- delete_projects = GoodData::Helpers.to_boolean(params.delete_projects)
35
- delete_extra = GoodData::Helpers.to_boolean(params.delete_extra)
36
-
37
34
  domain_name = params.organization || params.domain
38
35
  domain = client.domain(domain_name) || fail("Invalid domain name specified - #{domain_name}")
39
36
 
40
37
  domain.update_clients_settings(params.clients)
41
- domain.update_clients(params.clients, delete_extra: delete_extra, delete_projects: delete_projects)
38
+
39
+ delete_projects = GoodData::Helpers.to_boolean(params.delete_projects)
40
+ delete_extra = GoodData::Helpers.to_boolean(params.delete_extra)
41
+ options = { delete_projects: delete_projects }
42
+ options.merge!(delete_extra_option(params)) if delete_extra
43
+
44
+ domain.update_clients(params.clients, options)
45
+ end
46
+
47
+ private
48
+
49
+ def delete_extra_option(params)
50
+ if params.segments_filter && params.segments_filter.any?
51
+ { delete_extra_in_segments: params.segments_filter }
52
+ else
53
+ { delete_extra: delete_extra }
54
+ end
42
55
  end
43
56
  end
44
57
  end
@@ -65,11 +65,14 @@ module GoodData
65
65
  id: row[client_id_column],
66
66
  segment: segment_name,
67
67
  project: row[project_id_column],
68
- project_title: row[project_title_column],
69
68
  settings: [
70
69
  {
71
70
  name: 'lcm.token',
72
71
  value: row[project_token_column]
72
+ },
73
+ {
74
+ name: 'lcm.title',
75
+ value: row[project_title_column]
73
76
  }
74
77
  ]
75
78
  }.compact
@@ -66,6 +66,7 @@ module GoodData
66
66
  master_name = master.title
67
67
 
68
68
  sync_info = {
69
+ segment_id: segment.segment_id,
69
70
  from: master_pid,
70
71
  to: segment.clients.map do |segment_client|
71
72
  client_project = segment_client.project
@@ -30,10 +30,23 @@ module GoodData
30
30
  domain = client.domain(domain_name) || fail("Invalid domain name specified - #{domain_name}")
31
31
  domain_segments = domain.segments
32
32
 
33
+ if params.segments_filter
34
+ domain_segments.select! do |segment|
35
+ params.segments_filter.include?(segment.segment_id)
36
+ end
37
+ end
38
+
33
39
  segments = domain_segments.map do |segment|
34
- project = segment.master_project
40
+ project = nil
41
+
42
+ begin
43
+ project = segment.master_project
44
+ rescue RestClient::BadRequest => e
45
+ params.gdc_logger.error "Failed to retrieve master project for segment #{segment.id}. Error: #{e}"
46
+ raise
47
+ end
35
48
 
36
- # TODO: Check if project exists!
49
+ raise "Master project for segment #{segment.id} doesn't exist." unless project
37
50
 
38
51
  {
39
52
  segment_id: segment.segment_id,
@@ -21,8 +21,8 @@ module GoodData
21
21
  description 'ADS Client'
22
22
  param :ads_client, instance_of(Type::AdsClientType), required: true
23
23
 
24
- description 'Queries Used'
25
- param :query, instance_of(Type::ReleaseQueryType), required: false
24
+ # description 'Queries Used'
25
+ # param :query, instance_of(Type::ReleaseQueryType), required: false
26
26
 
27
27
  description 'Segments to manage'
28
28
  param :segments, array_of(instance_of(Type::SegmentType)), required: true
@@ -39,7 +39,6 @@ module GoodData
39
39
 
40
40
  domain_name = params.organization || params.domain
41
41
  domain = client.domain(domain_name) || fail("Invalid domain name specified - #{domain_name}")
42
- domain_segments = domain.segments
43
42
 
44
43
  synchronize_projects = []
45
44
  results = params.segments.map do |segment|
@@ -47,11 +46,10 @@ module GoodData
47
46
  Hash[m.each_pair.to_a].merge(type: :provision_result)
48
47
  end
49
48
 
50
- segment_master = domain_segments.find(segment.segment_id).first.master_project.pid
51
-
52
49
  unless tmp.empty?
53
50
  synchronize_projects << {
54
- from: segment_master,
51
+ segment_id: segment.segment_id,
52
+ from: segment.development_pid,
55
53
  to: tmp.map do |entry|
56
54
  {
57
55
  pid: entry[:project_uri].split('/').last,