gooddata 0.6.43 → 0.6.44

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 75045179773aed8468c7f91efed78c02acd931ba
4
- data.tar.gz: 88f27665f4934752bd0de316aeb2680ad8d3e22a
3
+ metadata.gz: 13caeda48d2131a8b475b6f40a499a4021781b9d
4
+ data.tar.gz: 3ae2f5d7baccbebc38f6b67ad36b0b84d534e8af
5
5
  SHA512:
6
- metadata.gz: bed8c32eb8cb93dde99409c46353b35204374b4f38a482a80f5da9b70d73af8f2b2c879d15aebfcf617fe5a999bcc1bee861567289bc36befd5d073f0d1bddc1
7
- data.tar.gz: 91b609b51dca26757d34d435c054a7e3936aa847a93dc17e388985b998a44917222c290784a2e09fe87e290de278a828ec3f7a369335721316fca59d3072f7f0
6
+ metadata.gz: 1e8db72abb19850d4586c8f689bedb987ab6c48c8ea03bbce232192d46c0a331f37c9c30847e069446ac9be9479322e5b7eca348d7bee16592db8e8ae142b347
7
+ data.tar.gz: 12d7328f77bdb39ea6bc950f372dd67b1e0ffc7e6c2c655722cb05532961275814f1c9b92dae324c5eb25fc8820ff9b5922c77e07187da632042dcbc8844d9f4
@@ -1,5 +1,10 @@
1
1
  # GoodData Ruby SDK Changelog
2
2
 
3
+ ## 0.6.44
4
+ - Fixed the tests
5
+ - The MAQL execution now throws an exception if there is an error
6
+ - The authentication is made via tokens not cookies
7
+
3
8
  ## 0.6.26
4
9
  - There is first implementation of transfering ETLs
5
10
  - Fixed bug with getting clients from domain
data/README.md CHANGED
@@ -21,7 +21,7 @@ The best documentation for the GoodData API can be found using these resources:
21
21
 
22
22
  ## Install
23
23
 
24
- If you are using bundler. Add
24
+ If you are using bundler, add
25
25
 
26
26
  gem "gooddata"
27
27
 
@@ -43,13 +43,13 @@ If you are using gems just
43
43
  future version unintentionally.
44
44
  * Commit, do not mess with rakefile, version, or history.
45
45
  (if you want to have your own version, that is fine but bump version in a commit by itself we can ignore when we pull)
46
- * run `rake test` and make sure all tests passes
47
- * run `rake cop` and make sure you did not introduced any new coding rules issues
46
+ * run `rake test` and make sure all tests pass
47
+ * run `rake cop` and make sure you did not introduce any new coding rules issues
48
48
  * Send us a pull request. Bonus points for topic branches.
49
49
 
50
50
  ## Credits
51
51
 
52
- **Originaly started by**
52
+ **Originally started by**
53
53
 
54
54
  Pavel Kolesnikov [ <mailto:pavel@gooddata.com> / [@koles](http://twitter.com/koles) ]
55
55
 
@@ -77,7 +77,7 @@ module GoodData
77
77
 
78
78
  fail(CommandFailed, "Usage: #{$PROGRAM_NAME} <dataset config>") unless cfg_file
79
79
  config = begin
80
- JSON.load open(cfg_file)
80
+ JSON.parse open(cfg_file)
81
81
  rescue
82
82
  raise(CommandFailed, "Error reading dataset config file '#{cfg_file}'")
83
83
  end
@@ -103,7 +103,7 @@ module GoodData
103
103
  file, cfg_file = args
104
104
  fail(CommandFailed, "Usage: #{$PROGRAM_NAME} datasets:load <file> <dataset config>") unless cfg_file
105
105
  begin
106
- config = JSON.load open(cfg_file)
106
+ config = JSON.parse open(cfg_file)
107
107
  rescue
108
108
  raise(CommandFailed, "Error reading dataset config file '#{cfg_file}'")
109
109
  end
@@ -13,7 +13,7 @@ module GoodData
13
13
  DEFAULT_LOG_OUTPUT = STDOUT
14
14
  DEFAULT_LOGGER_CLASS = Logger
15
15
 
16
- DEFAULT_RESTLOG_LEVEL = Logger::INFO
16
+ DEFAULT_RESTLOG_LEVEL = Logger::DEBUG
17
17
  DEFAULT_RESTLOG_OUTPUT = STDOUT
18
18
  DEFAULT_RESTLOGGER_CLASS = Logger
19
19
 
@@ -0,0 +1,16 @@
1
+ # encoding: UTF-8
2
+ #
3
+ # Copyright (c) 2010-2015 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
+ class MaqlExecutionError < RuntimeError
9
+ attr_accessor :data
10
+
11
+ def initialize(message, data)
12
+ super(message || 'Execution of maql failed')
13
+ @data = data
14
+ end
15
+ end
16
+ end
@@ -1,7 +1,7 @@
1
1
  class String
2
2
  def to_b
3
3
  return true if self == true || self =~ (/(true|t|yes|y|1)$/i)
4
- return false if self == false || self.blank? || self =~ (/(false|f|no|n|0)$/i)
5
- raise ArgumentError.new("invalid value for Boolean: \"#{self}\"")
4
+ return false if self == false || blank? || self =~ (/(false|f|no|n|0)$/i)
5
+ raise ArgumentError, "invalid value for Boolean: \"#{self}\""
6
6
  end
7
- end
7
+ end
@@ -265,7 +265,7 @@ module GoodData
265
265
  # @param [Object] Something
266
266
  # @return [Boolean] Returns true or false if the input is 'true' or true
267
267
  def to_boolean(param)
268
- (param == 'true' || param == true) ? true : false
268
+ param == 'true' || param == true ? true : false
269
269
  end
270
270
 
271
271
  # encrypts data with the given key. returns a binary data with the
@@ -283,10 +283,10 @@ module GoodData
283
283
  Base64.encode64(random_iv + encrypted)
284
284
  end
285
285
 
286
- def decrypt(data_base_64, key)
286
+ def decrypt(database64, key)
287
287
  return '' if key.nil? || key.empty?
288
288
 
289
- data = Base64.decode64(data_base_64)
289
+ data = Base64.decode64(database64)
290
290
 
291
291
  cipher = OpenSSL::Cipher::Cipher.new('aes-256-cbc')
292
292
  cipher.decrypt
@@ -26,7 +26,7 @@ module GoodData
26
26
  def encode_params(params, data_key)
27
27
  res = {}
28
28
  nested = {}
29
- core_types = [FalseClass, Fixnum, Float, NilClass, TrueClass, String]
29
+ core_types = [FalseClass, Integer, Float, NilClass, TrueClass, String]
30
30
  params.each do |k, v|
31
31
  if core_types.include?(v.class)
32
32
  res[k] = v
@@ -18,9 +18,9 @@ module GoodData
18
18
  next if !filter_on_segment.empty? && !(filter_on_segment.include?(segment.id))
19
19
  p = client.project
20
20
  begin
21
- p.create_users(migration_spec[:technical_user].map { |u| {login: u, role: 'admin'} })
21
+ p.create_users(migration_spec[:technical_user].map { |u| { login: u, role: 'admin' } })
22
22
  rescue RestClient::Exception => e
23
- messages << {type: :technical_user_addition, status: 'ERROR', message: e.message}
23
+ messages << { type: :technical_user_addition, status: 'ERROR', message: e.message }
24
24
  end
25
25
  end
26
26
  end
@@ -91,14 +91,12 @@ module GoodData
91
91
  def transfer_label_types(source_project, targets)
92
92
  semaphore = Mutex.new
93
93
 
94
- synchronized_puts = Proc.new do |*args|
95
- semaphore.synchronize {
96
- puts args
97
- }
94
+ synchronized_puts = proc do |*args|
95
+ semaphore.synchronize { puts args }
98
96
  end
99
97
 
100
98
  # Convert to array
101
- targets = [targets] unless targets.kind_of?(Array)
99
+ targets = [targets] unless targets.is_a?(Array)
102
100
 
103
101
  client = source_project.client
104
102
 
@@ -126,10 +124,10 @@ module GoodData
126
124
  # Transfer to target projects
127
125
  targets.peach do |target|
128
126
  transfer.peach do |identifier, type|
129
- uri = GoodData::MdObject.identifier_to_uri({project: target, client: client}, identifier)
127
+ uri = GoodData::MdObject.identifier_to_uri({ project: target, client: client }, identifier)
130
128
  next unless uri
131
129
 
132
- obj = GoodData::MdObject[uri, {project: target, client: client}]
130
+ obj = GoodData::MdObject[uri, { project: target, client: client }]
133
131
 
134
132
  if obj.content['type'] != type
135
133
  synchronized_puts.call "Updating #{identifier} -> #{type} in #{target.title} - #{target.uri}"
@@ -34,8 +34,8 @@ module GoodData
34
34
  end
35
35
  end
36
36
 
37
- def respond_to?(method_sym, *arguments, &block)
38
- if @data.key?(method_sym)
37
+ def respond_to_missing?(method_name, include_private = false)
38
+ if @data.key?(method_name)
39
39
  true
40
40
  else
41
41
  super
@@ -401,7 +401,7 @@ module GoodData
401
401
  #
402
402
  # @return [Array] array of errors
403
403
  def validate_some_anchors
404
- find_columns_by_type(:anchor).count == 0 ? [{ type: :no_anchor, dataset: id }] : []
404
+ find_columns_by_type(:anchor).count.zero? ? [{ type: :no_anchor, dataset: id }] : []
405
405
  end
406
406
 
407
407
  # Validate if the dataset does not have more than one anchor defined.
@@ -391,7 +391,7 @@ module GoodData
391
391
  res = client.poll_on_code(res['asyncTask']['links']['poll'])
392
392
  failed_count = GoodData::Helpers.get_path(res, %w(clientProjectProvisioningResult failed count), 0)
393
393
  created_count = GoodData::Helpers.get_path(res, %w(clientProjectProvisioningResult created count), 0)
394
- return Enumerator.new([]) if failed_count + created_count == 0
394
+ return Enumerator.new([]) if failed_count + created_count == 0 # rubocop:disable Style/NumericPredicate
395
395
  Enumerator.new do |y|
396
396
  uri = GoodData::Helpers.get_path(res, %w(clientProjectProvisioningResult links details))
397
397
  loop do
@@ -63,8 +63,8 @@ module GoodData
63
63
  c.create(self, json)
64
64
  end
65
65
 
66
- def diff_list(list_1, list_2)
67
- GoodData::Helpers.diff(list_1, list_2, key: :login)
66
+ def diff_list(list1, list2)
67
+ GoodData::Helpers.diff(list1, list2, key: :login)
68
68
  end
69
69
  end
70
70
 
@@ -145,7 +145,7 @@ module GoodData
145
145
  def deprecated=(flag)
146
146
  if flag == '1' || flag == 1 || flag == true
147
147
  meta['deprecated'] = '1'
148
- elsif flag == '0' || flag == 0 || flag == false
148
+ elsif flag == '0' || flag == 0 || flag == false # rubocop:disable Style/NumericPredicate
149
149
  meta['deprecated'] = '0'
150
150
  else
151
151
  fail 'You have to provide flag as either 1 or "1" or 0 or "0" or true/false'
@@ -53,7 +53,7 @@ module GoodData
53
53
  end
54
54
 
55
55
  def create(dashboard = {}, options = { :client => GoodData.client, :project => GoodData.project })
56
- client, project = GoodData.get_client_and_project(options)
56
+ client, project = GoodData.get_client_and_project(GoodData::Helpers.stringify_keys(options))
57
57
 
58
58
  res = client.create(Dashboard, GoodData::Helpers.deep_dup(GoodData::Helpers.deep_stringify_keys(EMPTY_OBJECT)), :project => project)
59
59
  dashboard.each do |k, v|
@@ -20,27 +20,27 @@ module GoodData
20
20
  # @param options [Hash] the options hash
21
21
  # @option options [Boolean] :full if passed true the subclass can decide to pull in full objects. This is desirable from the usability POV but unfortunately has negative impact on performance so it is not the default
22
22
  # @return [Array<GoodData::MdObject> | Array<Hash>] Return the appropriate metadata objects or their representation
23
- def all(options = {:client => GoodData.connection, :project => GoodData.project})
23
+ def all(options = { :client => GoodData.connection, :project => GoodData.project })
24
24
  query('folder', Folder, options)
25
25
  end
26
26
  end
27
27
 
28
28
  def entries
29
- (self.json['folder']['content']['entries'] || []).pmap do |entry|
30
- res = case self.json['folder']['content']['type'].first
31
- when 'fact'
32
- GoodData::Fact[entry['link'], :client => self.client, :project => self.project]
33
- when 'metric'
34
- GoodData::Metric[entry['link'], :client => self.client, :project => self.project]
35
- else
36
- GoodData::MdObject[entry['link'], :client => self.client, :project => self.project]
29
+ (json['folder']['content']['entries'] || []).pmap do |entry|
30
+ res = case json['folder']['content']['type'].first
31
+ when 'fact'
32
+ GoodData::Fact[entry['link'], :client => client, :project => project]
33
+ when 'metric'
34
+ GoodData::Metric[entry['link'], :client => client, :project => project]
35
+ else
36
+ GoodData::MdObject[entry['link'], :client => client, :project => project]
37
37
  end
38
38
  res
39
39
  end
40
40
  end
41
41
 
42
42
  def type
43
- self.json['folder']['content']['type'][0]
43
+ json['folder']['content']['type'][0]
44
44
  end
45
45
  end
46
46
  end
@@ -94,7 +94,7 @@ module GoodData
94
94
  def deploy(path, options = { :client => GoodData.client, :project => GoodData.project })
95
95
  return deploy_brick(path, options) if path.to_s.start_with?(APP_STORE_URL)
96
96
 
97
- return deploy_from_appstore(path.to_s, options) if (path.to_s =~ /\${.*}:(.*)\/(.*):\//) == 0
97
+ return deploy_from_appstore(path.to_s, options) if (path.to_s =~ %r{\${.*}:(.*)\/(.*):\/}) == 0 # rubocop:disable Style/NumericPredicate
98
98
 
99
99
  client, project = GoodData.get_client_and_project(options)
100
100
 
@@ -146,7 +146,7 @@ module GoodData
146
146
  if ref
147
147
  `git checkout #{ref}`
148
148
 
149
- fail 'Wrong branch or tag specified!' if $CHILD_STATUS.to_i != 0
149
+ fail 'Wrong branch or tag specified!' if $CHILD_STATUS.to_i.nonzero?
150
150
  end
151
151
 
152
152
  opts = {
@@ -167,7 +167,7 @@ module GoodData
167
167
  end
168
168
  end
169
169
 
170
- def deploy_from_appstore(path, options = {:client => GoodData.client, :project => GoodData.project})
170
+ def deploy_from_appstore(path, options = { :client => GoodData.client, :project => GoodData.project })
171
171
  client, project = GoodData.get_client_and_project(options)
172
172
 
173
173
  deploy_name = options[:name]
@@ -203,8 +203,6 @@ module GoodData
203
203
  puts HighLine.color("Deploy DONE #{path}", HighLine::GREEN) if verbose
204
204
  process
205
205
  end
206
-
207
- # ----------------------------- Private Stuff
208
206
 
209
207
  private
210
208
 
@@ -93,14 +93,14 @@ module GoodData
93
93
  res
94
94
  end
95
95
 
96
- def diff(item_1, item_2)
97
- x = diff_list([item_1], [item_2])
96
+ def diff(item1, item2)
97
+ x = diff_list([item1], [item2])
98
98
  return {} if x[:changed].empty?
99
99
  x[:changed].first[:diff]
100
100
  end
101
101
 
102
- def diff_list(list_1, list_2)
103
- GoodData::Helpers.diff(list_1, list_2, key: :login)
102
+ def diff_list(list1, list2)
103
+ GoodData::Helpers.diff(list1, list2, key: :login)
104
104
  end
105
105
 
106
106
  # Gets user currently logged in
@@ -590,7 +590,13 @@ module GoodData
590
590
  # @return [Array] Result of executing MAQLs
591
591
  def delete_all_data(options = {})
592
592
  return false unless options[:force]
593
- datasets.pmap(&:delete_data)
593
+ begin
594
+ datasets.pmap(&:delete_data)
595
+ rescue MaqlExecutionError => e
596
+ # This is here so that we do not throw out exceptions on synchornizing date dimensions
597
+ # Currently there is no reliable way how to tell it is a date dimension
598
+ fail e unless GoodData::Helpers.interpolate_error_messages(e.data['wTaskStatus']['messages']) == ["Internal error [handle_exception, hide_internal]."]
599
+ end
594
600
  end
595
601
 
596
602
  # Deletes dashboards for project
@@ -627,9 +633,13 @@ module GoodData
627
633
  response = client.post(ldm_uri, manage: { maql: maql })
628
634
  polling_uri = response['entries'].first['link']
629
635
 
630
- client.poll_on_response(polling_uri, options) do |body|
636
+ result = client.poll_on_response(polling_uri, options) do |body|
631
637
  body && body['wTaskStatus'] && body['wTaskStatus']['status'] == 'RUNNING'
632
638
  end
639
+ if result['wTaskStatus']['status'] == 'ERROR'
640
+ fail MaqlExecutionError.new("Executionof MAQL '#{maql}' failed in project '#{pid}'", result)
641
+ end
642
+ result
633
643
  end
634
644
 
635
645
  # Helper for getting facts of a project
@@ -749,11 +759,11 @@ module GoodData
749
759
  alias_method :member, :get_user
750
760
 
751
761
  def find_by_tag(tags)
752
- tags = tags.split(',').map(&:strip) unless tags.kind_of?(Array)
762
+ tags = tags.split(',').map(&:strip) unless tags.is_a?(Array)
753
763
 
754
764
  objects = tags.map do |tag|
755
- url = "/gdc/md/#{self.pid}/tags/#{tag}"
756
- res = self.client.get(url)
765
+ url = "/gdc/md/#{pid}/tags/#{tag}"
766
+ res = client.get(url)
757
767
 
758
768
  ((res || {})['entries'] || []).map do |entry|
759
769
  entry['link']
@@ -818,7 +828,7 @@ module GoodData
818
828
  puts "Inviting #{email}, role: #{role}"
819
829
 
820
830
  role_url = nil
821
- if role.index('/gdc/') != 0
831
+ if role.index('/gdc/').nonzero?
822
832
  tmp = get_role(role)
823
833
  role_url = tmp.uri if tmp
824
834
  else
@@ -1491,7 +1501,7 @@ module GoodData
1491
1501
  client.post("#{uri}/users", 'users' => payload)
1492
1502
  end
1493
1503
  # this ugly line turns the hash of errors into list of errors with types so we can process them easily
1494
- typed_results = results.flat_map { |x| x['projectUsersUpdateResult'].flat_map { |k, v| v.map { |v_2| v_2.is_a?(String) ? { type: k.to_sym, user: v_2 } : GoodData::Helpers.symbolize_keys(v_2).merge(type: k.to_sym) } } }
1504
+ typed_results = results.flat_map { |x| x['projectUsersUpdateResult'].flat_map { |k, v| v.map { |v2| v2.is_a?(String) ? { type: k.to_sym, user: v2 } : GoodData::Helpers.symbolize_keys(v2).merge(type: k.to_sym) } } }
1495
1505
  # we have to concat errors from role resolution and API result
1496
1506
  typed_results + (users_by_type[:failed] || [])
1497
1507
  end
@@ -39,58 +39,36 @@ module GoodData
39
39
  def migrate_datasets(spec, opts = {})
40
40
  opts = { client: GoodData.connection }.merge(opts)
41
41
  dry_run = opts[:dry_run]
42
+ replacements = opts['maql_replacements'] || opts[:maql_replacements] || {}
42
43
 
43
44
  client, project = GoodData.get_client_and_project(opts)
44
45
 
45
46
  bp = ProjectBlueprint.new(spec)
47
+
46
48
  uri = "/gdc/projects/#{project.pid}/model/diff?includeGrain=true"
47
49
  result = client.post(uri, bp.to_wire)
50
+ response = client.poll_on_code(result['asyncTask']['link']['poll'])
48
51
 
49
- link = result['asyncTask']['link']['poll']
50
- response = client.get(link, :process => false)
51
-
52
- while response.code != 200
53
- sleep 1
54
- GoodData::Rest::Client.retryable(:tries => 3) do
55
- sleep 1
56
- response = client.get(link, :process => false)
57
- end
58
- end
59
-
60
- response = client.get(link)
61
-
62
- errors = []
63
52
  maqls = pick_correct_chunks(response['projectModelDiff']['updateScripts'], opts)
64
- if !maqls.empty? && !dry_run
65
- maqls.each_with_index do |maql, _idx|
53
+ replaced_maqls = apply_replacements_on_maql(maqls, replacements)
54
+
55
+ unless dry_run
56
+ errors = []
57
+ replaced_maqls.each do |replaced_maql_chunks|
66
58
  begin
67
- chunks = maql[:orig]['updateScript']['maqlDdlChunks']
68
- chunks.each do |chunk|
69
- # TODO: Hack the MAQL here
70
- (opts[:maql_replacements] || opts['maql_replacements'] || {}).each do |k, v|
71
- src = Regexp.new(k)
72
- dest = v
73
- chunk.gsub!(src, dest)
74
- end
75
-
76
- puts chunk
77
-
78
- result = project.execute_maql(chunk)
79
- if result['wTaskStatus']['status'] == 'ERROR'
80
- puts JSON.pretty_generate(result)
81
- fail 'Creating dataset failed'
82
- end
83
- end
84
- return chunks
59
+ replaced_maql_chunks['updateScript']['maqlDdlChunks'].each { |chunk| project.execute_maql(chunk) }
85
60
  rescue => e
86
- puts "Error occured when executing MAQL, project: \"#{project.title}\" reason: \"#{e.message}\", chunks: #{chunks.inspect}"
61
+ puts "Error occured when executing MAQL, project: \"#{project.title}\" reason: \"#{e.message}\", chunks: #{replaced_maql_chunks.inspect}"
87
62
  errors << e
88
63
  next
89
64
  end
90
65
  end
91
-
92
- fail "Unable to migrate LDM, reason(s): #{JSON.pretty_generate(errors)}" unless errors.empty?
66
+ if errors.length == replaced_maqls.length
67
+ messages = errors.map { |e| GoodData::Helpers.interpolate_error_messages(e.data['wTaskStatus']['messages']) }
68
+ fail "Unable to migrate LDM, reason(s): \n #{messages.join("\n")}"
69
+ end
93
70
  end
71
+ replaced_maqls
94
72
  end
95
73
 
96
74
  def migrate_reports(project, spec)
@@ -130,7 +108,7 @@ module GoodData
130
108
  end
131
109
 
132
110
  def pick_correct_chunks(chunks, opts = {})
133
- preference = opts[:update_preference]
111
+ preference = GoodData::Helpers.symbolize_keys(opts[:update_preference] || {})
134
112
 
135
113
  # first is cascadeDrops, second is preserveData
136
114
  rules = [
@@ -150,14 +128,24 @@ module GoodData
150
128
 
151
129
  results = GoodData::Helpers.join(rules, stuff, [:cascade_drops, :preserve_data], [:cascade_drops, :preserve_data], inner: true).sort_by { |l| l[:priority] } || []
152
130
 
153
- (preference || {}).each do |k, v|
131
+ preference.each do |k, v|
154
132
  results = results.find_all do |result|
155
- sym = k.to_sym
156
- !result.has_key?(sym) || result[sym] == v
133
+ result[k] == v
157
134
  end
158
135
  end
136
+ (preference.empty? ? [results.first].compact : results).map { |result| result[:orig] }
137
+ end
138
+
139
+ private
159
140
 
160
- preference ? results : [results.first]
141
+ def apply_replacements_on_maql(maqls, replacements = {})
142
+ maqls.map do |maql|
143
+ GoodData::Helpers.deep_dup(maql).tap do |m|
144
+ m['updateScript']['maqlDdlChunks'] = m['updateScript']['maqlDdlChunks'].map do |chunk|
145
+ replacements.reduce(chunk) { |a, (k, v)| a.gsub(Regexp.new(k), v) }
146
+ end
147
+ end
148
+ end
161
149
  end
162
150
  end
163
151
  end