gooddata 0.6.16 → 0.6.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +4 -1
  3. data/lib/gooddata/cli/commands/project_cmd.rb +1 -1
  4. data/lib/gooddata/core/logging.rb +15 -5
  5. data/lib/gooddata/core/rest.rb +4 -28
  6. data/lib/gooddata/helpers/global_helpers.rb +14 -138
  7. data/lib/gooddata/helpers/global_helpers_params.rb +145 -0
  8. data/lib/gooddata/mixins/md_object_indexer.rb +2 -2
  9. data/lib/gooddata/models/domain.rb +1 -1
  10. data/lib/gooddata/models/execution.rb +29 -1
  11. data/lib/gooddata/models/from_wire.rb +6 -0
  12. data/lib/gooddata/models/from_wire_parse.rb +125 -0
  13. data/lib/gooddata/models/metadata/attribute.rb +1 -1
  14. data/lib/gooddata/models/metadata/label.rb +11 -10
  15. data/lib/gooddata/models/model.rb +4 -0
  16. data/lib/gooddata/models/profile.rb +12 -2
  17. data/lib/gooddata/models/project.rb +6 -3
  18. data/lib/gooddata/models/project_blueprint.rb +4 -4
  19. data/lib/gooddata/models/project_creator.rb +8 -10
  20. data/lib/gooddata/models/report_data_result.rb +4 -2
  21. data/lib/gooddata/models/schedule.rb +121 -66
  22. data/lib/gooddata/models/to_wire.rb +12 -3
  23. data/lib/gooddata/models/user_filters/user_filter_builder.rb +3 -234
  24. data/lib/gooddata/models/user_filters/user_filter_builder_create.rb +115 -0
  25. data/lib/gooddata/models/user_filters/user_filter_builder_execute.rb +133 -0
  26. data/lib/gooddata/rest/client.rb +27 -13
  27. data/lib/gooddata/rest/connection.rb +102 -23
  28. data/lib/gooddata/version.rb +1 -1
  29. data/spec/data/gd_gse_data_blueprint.json +1 -0
  30. data/spec/data/test_project_model_spec.json +5 -2
  31. data/spec/data/wire_models/model_view.json +3 -0
  32. data/spec/data/wire_test_project.json +8 -1
  33. data/spec/integration/full_project_spec.rb +1 -1
  34. data/spec/unit/core/connection_spec.rb +16 -0
  35. data/spec/unit/core/logging_spec.rb +54 -6
  36. data/spec/unit/models/domain_spec.rb +10 -4
  37. data/spec/unit/models/execution_spec.rb +102 -0
  38. data/spec/unit/models/from_wire_spec.rb +11 -2
  39. data/spec/unit/models/model_spec.rb +2 -2
  40. data/spec/unit/models/project_blueprint_spec.rb +1 -1
  41. data/spec/unit/models/schedule_spec.rb +34 -24
  42. data/spec/unit/models/to_wire_spec.rb +9 -1
  43. metadata +8 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 40814e8c9d7deee9ac0eca814a3ac3c635f1ede3
4
- data.tar.gz: fe14bb4f38e410de2e7abc9624e5f3aba6c39e6f
3
+ metadata.gz: 0bbc18e2feb9dc7d8f7b2fcadfd4e26474b666d0
4
+ data.tar.gz: 0020310a69543fcea52bd5d0bee0ddbe25bae3ff
5
5
  SHA512:
6
- metadata.gz: f4128f12fab224bc8a5814fe0dcee35c385c2652defe34afb331562a65332b7f4fdd9199b7bb4c653eebdff39d6dd21950482e10fb1b51c5e81664072c490035
7
- data.tar.gz: 731f9c3dab1e859777931f44362e4e50c2bc79fa562a4172ad2d53c658d9b3f309ff2000fb36a9090bdecd09be5240d76d3344316d60b281803bcc3d4e90373e
6
+ metadata.gz: dc66e78d316ee699b2374f1885a8cd6736addd8040931400e7fab8c287327a322828480fbd0d25a5ebaea343c8b810e926afa174b2f39239d1aec0e9f504114a
7
+ data.tar.gz: 388b87c526902279fd6c01b5739f478e3d8d4787179e3062ae495caa0809ad58d47b3edaee21230425f373d5ab056097e20214b568038da7cdd07053f981b3c8
@@ -4,6 +4,9 @@ AbcSize:
4
4
  ClassLength:
5
5
  Enabled: false
6
6
 
7
+ ModuleLength:
8
+ Enabled: false
9
+
7
10
  CyclomaticComplexity:
8
11
  Enabled: false
9
12
 
@@ -29,4 +32,4 @@ MethodLength:
29
32
  Enabled: false
30
33
 
31
34
  PerceivedComplexity:
32
- Enabled: false
35
+ Enabled: false
@@ -130,7 +130,7 @@ GoodData::CLI.module_eval do
130
130
  show.action do |global_options, options, _args|
131
131
  opts = options.merge(global_options)
132
132
  client = GoodData.connect(opts)
133
- spec, _ = GoodData::Command::Project.get_spec_and_project_id('.')
133
+ spec = GoodData::Command::Project.get_spec_and_project_id('.')[0]
134
134
  new_project = GoodData::Command::Project.build(opts.merge(spec: spec, client: client))
135
135
  puts "Project was created. New project PID is #{new_project.pid}, URI is #{new_project.uri}."
136
136
  end
@@ -14,7 +14,7 @@ module GoodData
14
14
  # GoodData.logging_on
15
15
  #
16
16
  def logging_on
17
- GoodData.logger = Logger.new(STDOUT) if logger.is_a? NilLogger
17
+ @logger = default_logger if logger.is_a? NilLogger
18
18
  end
19
19
 
20
20
  # Turn logging on
@@ -24,15 +24,15 @@ module GoodData
24
24
  # GoodData.logging_off
25
25
  #
26
26
  def logging_off
27
- GoodData.logger = NilLogger.new
27
+ @logger = NilLogger.new
28
28
  end
29
29
 
30
30
  def logging_on?
31
- !GoodData.logger.instance_of?(NilLogger)
31
+ !@logger.instance_of?(NilLogger)
32
32
  end
33
33
 
34
34
  # Returns the logger instance. The default implementation
35
- # does not log anything
35
+ # is a logger to stdout on INFO level
36
36
  # For some serious logging, set the logger instance using
37
37
  # the logger= method
38
38
  #
@@ -42,7 +42,7 @@ module GoodData
42
42
  # GoodData.logger = Logger.new(STDOUT)
43
43
  #
44
44
  def logger
45
- @logger ||= NilLogger.new
45
+ @logger ||= default_logger
46
46
  end
47
47
 
48
48
  def stats_on
@@ -56,5 +56,15 @@ module GoodData
56
56
  def stats_off
57
57
  @stats = false
58
58
  end
59
+
60
+ private
61
+
62
+ # The default logger - stdout and INFO level
63
+ #
64
+ def default_logger
65
+ log = Logger.new(STDOUT)
66
+ log.level = Logger::INFO
67
+ log
68
+ end
59
69
  end
60
70
  end
@@ -1,8 +1,6 @@
1
1
  # encoding: UTF-8
2
2
 
3
3
  module GoodData
4
- DEFAULT_SLEEP_INTERVAL = 10
5
-
6
4
  class << self
7
5
  # Performs a HTTP GET request.
8
6
  #
@@ -122,21 +120,9 @@ module GoodData
122
120
  # @param options [Hash] Options
123
121
  # @return [Hash] Result of polling
124
122
  def poll_on_code(link, options = {})
125
- code = options[:code] || 202
126
- sleep_interval = options[:sleep_interval] || DEFAULT_SLEEP_INTERVAL
127
- response = GoodData.get(link, :process => false)
128
- while response.code == code
129
- sleep sleep_interval
130
- GoodData::Rest::Client.retryable(:tries => 3, :refresh_token => proc { connection.refresh_token }) do
131
- sleep sleep_interval
132
- response = GoodData.get(link, :process => false)
133
- end
134
- end
135
- if options[:process] == false
136
- response
137
- else
138
- GoodData.get(link)
139
- end
123
+ client = options[:client]
124
+ fail ArgumentError, 'No :client specified' if client.nil?
125
+ client.poll_on_code(link, options)
140
126
  end
141
127
 
142
128
  # Generalizaton of poller. Since we have quite a variation of how async proceses are handled
@@ -151,17 +137,7 @@ module GoodData
151
137
  def poll_on_response(link, options = {}, &bl)
152
138
  client = options[:client]
153
139
  fail ArgumentError, 'No :client specified' if client.nil?
154
-
155
- sleep_interval = options[:sleep_interval] || DEFAULT_SLEEP_INTERVAL
156
- response = get(link)
157
- while bl.call(response)
158
- sleep sleep_interval
159
- GoodData::Rest::Client.retryable(:tries => 3, :refresh_token => proc { client.connection.refresh_token }) do
160
- sleep sleep_interval
161
- response = get(link)
162
- end
163
- end
164
- response
140
+ client.poll_on_response(link, options, &bl)
165
141
  end
166
142
 
167
143
  private
@@ -3,147 +3,11 @@
3
3
  require 'active_support/all'
4
4
  require 'pathname'
5
5
 
6
+ require_relative 'global_helpers_params'
7
+
6
8
  module GoodData
7
9
  module Helpers
8
10
  class << self
9
- # A helper which allows you to diff two lists of objects. The objects
10
- # can be arbitrary objects as long as they respond to to_hash because
11
- # the diff is eventually done on hashes. It allows you to specify
12
- # several options to allow you to limit on what the sameness test is done
13
- #
14
- # @param [Array<Object>] old_list List of objects that serves as a base for comparison
15
- # @param [Array<Object>] new_list List of objects that is compared agianst the old_list
16
- # @return [Hash] A structure that contains the result of the comparison. There are
17
- # four keys.
18
- # :added contains the list that are in new_list but were not in the old_list
19
- # :added contains the list that are in old_list but were not in the new_list
20
- # :same contains objects that are in both lists and they are the same
21
- # :changed contains list of objects that changed along ith original, the new one
22
- # and the list of changes
23
- def diff(old_list, new_list, options = {})
24
- old_list = old_list.map(&:to_hash)
25
- new_list = new_list.map(&:to_hash)
26
-
27
- fields = options[:fields]
28
- lookup_key = options[:key]
29
-
30
- old_lookup = Hash[old_list.map { |v| [v[lookup_key], v] }]
31
-
32
- res = {
33
- :added => [],
34
- :removed => [],
35
- :changed => [],
36
- :same => []
37
- }
38
-
39
- new_list.each do |new_obj|
40
- old_obj = old_lookup[new_obj[lookup_key]]
41
- if old_obj.nil?
42
- res[:added] << new_obj
43
- next
44
- end
45
-
46
- if fields
47
- sliced_old_obj = old_obj.slice(*fields)
48
- sliced_new_obj = new_obj.slice(*fields)
49
- else
50
- sliced_old_obj = old_obj
51
- sliced_new_obj = new_obj
52
- end
53
- if sliced_old_obj != sliced_new_obj
54
- difference = sliced_new_obj.to_a - sliced_old_obj.to_a
55
- differences = Hash[*difference.mapcat { |x| x }]
56
- res[:changed] << {
57
- old_obj: old_obj,
58
- new_obj: new_obj,
59
- diff: differences
60
- }
61
- else
62
- res[:same] << old_obj
63
- end
64
- end
65
-
66
- new_lookup = Hash[new_list.map { |v| [v[lookup_key], v] }]
67
- old_list.each do |old_obj|
68
- new_obj = new_lookup[old_obj[lookup_key]]
69
- if new_obj.nil?
70
- res[:removed] << old_obj
71
- next
72
- end
73
- end
74
-
75
- res
76
- end
77
-
78
- def create_lookup(collection, on)
79
- lookup = {}
80
- if on.is_a?(Array)
81
- collection.each do |e|
82
- key = e.values_at(*on)
83
- lookup[key] = [] unless lookup.key?(key)
84
- lookup[key] << e
85
- end
86
- else
87
- collection.each do |e|
88
- key = e[on]
89
- lookup[key] = [] unless lookup.key?(key)
90
- lookup[key] << e
91
- end
92
- end
93
- lookup
94
- end
95
-
96
- ENCODED_PARAMS_KEY = :gd_encoded_params
97
- ENCODED_HIDDEN_PARAMS_KEY = :gd_encoded_hidden_params
98
-
99
- # Encodes parameters for passing them to GD execution platform.
100
- # Core types are kept and complex types (arrays, structures, etc) are JSON encoded into key hash "gd_encoded_params" or "gd_encoded_hidden_params", depending on the 'hidden' method param.
101
- # The two different keys are used because the params and hidden params are merged by the platform and if we use the same key, the param would be overwritten.
102
- #
103
- # Core types are following:
104
- # - Boolean (true, false)
105
- # - Fixnum
106
- # - Float
107
- # - Nil
108
- # - String
109
- #
110
- # @param [Hash] params Parameters to be encoded
111
- # @return [Hash] Encoded parameters
112
- def encode_params(params, hidden = false)
113
- res = {}
114
- nested = {}
115
- core_types = [FalseClass, Fixnum, Float, NilClass, TrueClass, String]
116
- params.each do |k, v|
117
- if core_types.include?(v.class)
118
- res[k] = v
119
- else
120
- nested[k] = v
121
- end
122
- end
123
- key = hidden ? ENCODED_HIDDEN_PARAMS_KEY : ENCODED_PARAMS_KEY
124
- res[key] = nested.to_json unless nested.empty?
125
- res
126
- end
127
-
128
- # Decodes params as they came from the platform
129
- # The "data" key is supposed to be json and it's parsed - if this
130
- def decode_params(params)
131
- key = ENCODED_PARAMS_KEY.to_s
132
- hidden_key = ENCODED_HIDDEN_PARAMS_KEY.to_s
133
- data_params = params[key] || '{}'
134
- hidden_data_params = params[hidden_key] || '{}'
135
-
136
- begin
137
- parsed_data_params = JSON.parse(data_params)
138
- parsed_hidden_data_params = JSON.parse(hidden_data_params)
139
- rescue JSON::ParserError => e
140
- raise e.class, "Error reading json from '#{key}' or '#{hidden_key}' in params #{params}\n #{e.message}"
141
- end
142
- params.delete(key)
143
- params.delete(hidden_key)
144
- params.merge(parsed_data_params).merge(parsed_hidden_data_params)
145
- end
146
-
147
11
  def error(msg)
148
12
  STDERR.puts(msg)
149
13
  exit 1
@@ -252,6 +116,18 @@ module GoodData
252
116
  h
253
117
  end
254
118
  end
119
+
120
+ def stringify_keys_deep!(h)
121
+ if Hash == h.class
122
+ Hash[
123
+ h.map do |k, v|
124
+ [k.respond_to?(:to_s) ? k.to_s : k, stringify_keys_deep!(v)]
125
+ end
126
+ ]
127
+ else
128
+ h
129
+ end
130
+ end
255
131
  end
256
132
  end
257
133
  end
@@ -0,0 +1,145 @@
1
+ # encoding: UTF-8
2
+
3
+ module GoodData
4
+ module Helpers
5
+ class << self
6
+ ENCODED_PARAMS_KEY = :gd_encoded_params
7
+ ENCODED_HIDDEN_PARAMS_KEY = :gd_encoded_hidden_params
8
+
9
+ # Encodes parameters for passing them to GD execution platform.
10
+ # Core types are kept and complex types (arrays, structures, etc) are JSON encoded into key hash "gd_encoded_params" or "gd_encoded_hidden_params", depending on the 'hidden' method param.
11
+ # The two different keys are used because the params and hidden params are merged by the platform and if we use the same key, the param would be overwritten.
12
+ #
13
+ # Core types are following:
14
+ # - Boolean (true, false)
15
+ # - Fixnum
16
+ # - Float
17
+ # - Nil
18
+ # - String
19
+ #
20
+ # @param [Hash] params Parameters to be encoded
21
+ # @return [Hash] Encoded parameters
22
+ def encode_params(params, hidden = false)
23
+ res = {}
24
+ nested = {}
25
+ core_types = [FalseClass, Fixnum, Float, NilClass, TrueClass, String]
26
+ params.each do |k, v|
27
+ if core_types.include?(v.class)
28
+ res[k] = v
29
+ else
30
+ nested[k] = v
31
+ end
32
+ end
33
+ key = hidden ? ENCODED_HIDDEN_PARAMS_KEY : ENCODED_PARAMS_KEY
34
+ res[key] = nested.to_json unless nested.empty?
35
+ res
36
+ end
37
+
38
+ # Decodes params as they came from the platform
39
+ # The "data" key is supposed to be json and it's parsed - if this
40
+ def decode_params(params)
41
+ key = ENCODED_PARAMS_KEY.to_s
42
+ hidden_key = ENCODED_HIDDEN_PARAMS_KEY.to_s
43
+ data_params = params[key] || '{}'
44
+ hidden_data_params = params[hidden_key] || '{}'
45
+
46
+ begin
47
+ parsed_data_params = JSON.parse(data_params)
48
+ parsed_hidden_data_params = JSON.parse(hidden_data_params)
49
+ rescue JSON::ParserError => e
50
+ raise e.class, "Error reading json from '#{key}' or '#{hidden_key}' in params #{params}\n #{e.message}"
51
+ end
52
+ params.delete(key)
53
+ params.delete(hidden_key)
54
+ params.merge(parsed_data_params).merge(parsed_hidden_data_params)
55
+ end
56
+
57
+ # A helper which allows you to diff two lists of objects. The objects
58
+ # can be arbitrary objects as long as they respond to to_hash because
59
+ # the diff is eventually done on hashes. It allows you to specify
60
+ # several options to allow you to limit on what the sameness test is done
61
+ #
62
+ # @param [Array<Object>] old_list List of objects that serves as a base for comparison
63
+ # @param [Array<Object>] new_list List of objects that is compared agianst the old_list
64
+ # @return [Hash] A structure that contains the result of the comparison. There are
65
+ # four keys.
66
+ # :added contains the list that are in new_list but were not in the old_list
67
+ # :added contains the list that are in old_list but were not in the new_list
68
+ # :same contains objects that are in both lists and they are the same
69
+ # :changed contains list of objects that changed along ith original, the new one
70
+ # and the list of changes
71
+ def diff(old_list, new_list, options = {})
72
+ old_list = old_list.map(&:to_hash)
73
+ new_list = new_list.map(&:to_hash)
74
+
75
+ fields = options[:fields]
76
+ lookup_key = options[:key]
77
+
78
+ old_lookup = Hash[old_list.map { |v| [v[lookup_key], v] }]
79
+
80
+ res = {
81
+ :added => [],
82
+ :removed => [],
83
+ :changed => [],
84
+ :same => []
85
+ }
86
+
87
+ new_list.each do |new_obj|
88
+ old_obj = old_lookup[new_obj[lookup_key]]
89
+ if old_obj.nil?
90
+ res[:added] << new_obj
91
+ next
92
+ end
93
+
94
+ if fields
95
+ sliced_old_obj = old_obj.slice(*fields)
96
+ sliced_new_obj = new_obj.slice(*fields)
97
+ else
98
+ sliced_old_obj = old_obj
99
+ sliced_new_obj = new_obj
100
+ end
101
+ if sliced_old_obj != sliced_new_obj
102
+ difference = sliced_new_obj.to_a - sliced_old_obj.to_a
103
+ differences = Hash[*difference.mapcat { |x| x }]
104
+ res[:changed] << {
105
+ old_obj: old_obj,
106
+ new_obj: new_obj,
107
+ diff: differences
108
+ }
109
+ else
110
+ res[:same] << old_obj
111
+ end
112
+ end
113
+
114
+ new_lookup = Hash[new_list.map { |v| [v[lookup_key], v] }]
115
+ old_list.each do |old_obj|
116
+ new_obj = new_lookup[old_obj[lookup_key]]
117
+ if new_obj.nil?
118
+ res[:removed] << old_obj
119
+ next
120
+ end
121
+ end
122
+
123
+ res
124
+ end
125
+
126
+ def create_lookup(collection, on)
127
+ lookup = {}
128
+ if on.is_a?(Array)
129
+ collection.each do |e|
130
+ key = e.values_at(*on)
131
+ lookup[key] = [] unless lookup.key?(key)
132
+ lookup[key] << e
133
+ end
134
+ else
135
+ collection.each do |e|
136
+ key = e[on]
137
+ lookup[key] = [] unless lookup.key?(key)
138
+ lookup[key] << e
139
+ end
140
+ end
141
+ lookup
142
+ end
143
+ end
144
+ end
145
+ end