gooddata 0.6.16 → 0.6.17

Sign up to get free protection for your applications and to get access to all the features.
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