gooddata 0.6.11 → 0.6.12

Sign up to get free protection for your applications and to get access to all the features.
Files changed (104) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +6 -0
  3. data/.travis.yml +5 -0
  4. data/CHANGELOG.md +34 -1
  5. data/CLI.md +1 -1
  6. data/authors.sh +4 -0
  7. data/lib/gooddata.rb +1 -1
  8. data/lib/gooddata/cli/commands/api_cmd.rb +0 -2
  9. data/lib/gooddata/cli/commands/auth_cmd.rb +0 -3
  10. data/lib/gooddata/cli/commands/console_cmd.rb +1 -2
  11. data/lib/gooddata/cli/commands/domain_cmd.rb +0 -2
  12. data/lib/gooddata/cli/commands/process_cmd.rb +0 -2
  13. data/lib/gooddata/cli/commands/project_cmd.rb +0 -2
  14. data/lib/gooddata/cli/commands/projects_cmd.rb +0 -2
  15. data/lib/gooddata/cli/commands/run_ruby_cmd.rb +2 -3
  16. data/lib/gooddata/cli/commands/scaffold_cmd.rb +0 -3
  17. data/lib/gooddata/cli/commands/user_cmd.rb +0 -2
  18. data/lib/gooddata/cli/shared.rb +1 -2
  19. data/lib/gooddata/commands/datawarehouse.rb +24 -0
  20. data/lib/gooddata/commands/process.rb +0 -1
  21. data/lib/gooddata/commands/project.rb +1 -1
  22. data/lib/gooddata/commands/scaffold.rb +0 -1
  23. data/lib/gooddata/core/connection.rb +376 -0
  24. data/lib/gooddata/core/logging.rb +13 -0
  25. data/lib/gooddata/core/rest.rb +40 -16
  26. data/lib/gooddata/exceptions/user_in_different_domain.rb +11 -0
  27. data/lib/gooddata/extensions/enumerable.rb +8 -0
  28. data/lib/gooddata/goodzilla/goodzilla.rb +24 -0
  29. data/lib/gooddata/helpers/global_helpers.rb +126 -12
  30. data/lib/gooddata/mixins/author.rb +11 -5
  31. data/lib/gooddata/mixins/is_dimension.rb +13 -0
  32. data/lib/gooddata/mixins/md_object_indexer.rb +17 -1
  33. data/lib/gooddata/mixins/md_object_query.rb +10 -2
  34. data/lib/gooddata/mixins/md_relations.rb +2 -2
  35. data/lib/gooddata/mixins/rest_resource.rb +1 -0
  36. data/lib/gooddata/models/data_result.rb +0 -1
  37. data/lib/gooddata/models/datawarehouse.rb +90 -0
  38. data/lib/gooddata/models/domain.rb +202 -76
  39. data/lib/gooddata/models/execution.rb +11 -0
  40. data/lib/gooddata/models/from_wire.rb +4 -4
  41. data/lib/gooddata/models/invitation.rb +0 -5
  42. data/lib/gooddata/models/membership.rb +121 -91
  43. data/lib/gooddata/models/metadata.rb +1 -2
  44. data/lib/gooddata/models/metadata/attribute.rb +7 -0
  45. data/lib/gooddata/models/metadata/dashboard.rb +1 -1
  46. data/lib/gooddata/models/metadata/dimension.rb +52 -0
  47. data/lib/gooddata/models/metadata/fact.rb +1 -1
  48. data/lib/gooddata/models/metadata/label.rb +21 -7
  49. data/lib/gooddata/models/metadata/metric.rb +1 -23
  50. data/lib/gooddata/models/metadata/report.rb +2 -2
  51. data/lib/gooddata/models/metadata/report_definition.rb +22 -2
  52. data/lib/gooddata/models/metadata/variable.rb +81 -0
  53. data/lib/gooddata/models/model.rb +2 -1
  54. data/lib/gooddata/models/process.rb +3 -4
  55. data/lib/gooddata/models/profile.rb +50 -82
  56. data/lib/gooddata/models/project.rb +170 -213
  57. data/lib/gooddata/models/project_blueprint.rb +14 -5
  58. data/lib/gooddata/models/project_creator.rb +2 -2
  59. data/lib/gooddata/models/schedule.rb +10 -8
  60. data/lib/gooddata/models/to_wire.rb +2 -2
  61. data/lib/gooddata/models/user_filters/mandatory_user_filter.rb +67 -0
  62. data/lib/gooddata/models/user_filters/user_filter.rb +96 -0
  63. data/lib/gooddata/models/user_filters/user_filter_builder.rb +409 -0
  64. data/lib/gooddata/{rest/connections/connections.rb → models/user_filters/user_filters.rb} +1 -0
  65. data/lib/gooddata/models/user_filters/variable_user_filter.rb +14 -0
  66. data/lib/gooddata/rest/client.rb +32 -21
  67. data/lib/gooddata/rest/connection.rb +283 -11
  68. data/lib/gooddata/rest/connections/rest_client_connection.rb +47 -109
  69. data/lib/gooddata/version.rb +1 -1
  70. data/spec/data/column_based_permissions.csv +7 -0
  71. data/spec/data/column_based_permissions2.csv +6 -0
  72. data/spec/data/hello_world_process/hello_world.rb +3 -1
  73. data/spec/data/line_based_permissions.csv +3 -0
  74. data/spec/data/m_n_model/blueprint.json +76 -0
  75. data/spec/data/{model_view.json → wire_models/model_view.json} +0 -0
  76. data/spec/data/wire_models/nu_model.json +3046 -0
  77. data/spec/helpers/process_helper.rb +2 -2
  78. data/spec/helpers/project_helper.rb +29 -0
  79. data/spec/helpers/schedule_helper.rb +1 -1
  80. data/spec/integration/command_datawarehouse_spec.rb +32 -0
  81. data/spec/integration/create_project_spec.rb +0 -1
  82. data/spec/integration/full_process_schedule_spec.rb +13 -5
  83. data/spec/integration/full_project_spec.rb +2 -1
  84. data/spec/integration/over_to_user_filters_spec.rb +92 -0
  85. data/spec/integration/project_spec.rb +233 -0
  86. data/spec/integration/rest_spec.rb +209 -0
  87. data/spec/integration/user_filters_spec.rb +193 -0
  88. data/spec/integration/variables_spec.rb +196 -0
  89. data/spec/unit/commands/command_auth_spec.rb +0 -7
  90. data/spec/unit/commands/command_process_spec.rb +10 -13
  91. data/spec/unit/core/connection_spec.rb +0 -19
  92. data/spec/unit/helpers/global_helpers_spec.rb +57 -0
  93. data/spec/unit/models/domain_spec.rb +80 -40
  94. data/spec/unit/models/from_wire_spec.rb +8 -1
  95. data/spec/unit/models/params_spec.rb +6 -6
  96. data/spec/unit/models/profile_spec.rb +23 -22
  97. data/spec/unit/models/project_blueprint_spec.rb +1 -6
  98. data/spec/unit/models/project_spec.rb +331 -286
  99. data/spec/unit/models/schedule_spec.rb +39 -14
  100. data/spec/unit/models/user_filters_spec.rb +89 -0
  101. data/spec/unit/models/variable_spec.rb +259 -0
  102. metadata +31 -7
  103. data/lib/gooddata/rest/connections/dummy_connection.rb +0 -52
  104. data/spec/unit/core/rest_spec.rb +0 -106
@@ -66,6 +66,17 @@ module GoodData
66
66
  @json['execution']['links']['self'] if @json && @json['execution'] && @json['execution']['links']
67
67
  end
68
68
 
69
+ # Wait for execution result, status different than RUNNING or SCHEDULED
70
+ #
71
+ # @return [GoodData::Execution] Execution result
72
+ def wait_for_result
73
+ res = client.poll_on_response(uri) do |body|
74
+ body['execution'] && (body['execution']['status'] == 'RUNNING' || body['execution']['status'] == 'SCHEDULED')
75
+ end
76
+ @json = res
77
+ self
78
+ end
79
+
69
80
  # Compares two executions - based on their URI
70
81
  def ==(other)
71
82
  other.respond_to?(:uri) && other.uri == uri && other.respond_to?(:to_hash) && other.to_hash == to_hash
@@ -136,14 +136,14 @@ module GoodData
136
136
  if ref =~ /^dataset\./
137
137
  {
138
138
  :type => :reference,
139
- :name => ref.gsub('dataset.', ''),
140
- :dataset => ref.gsub('dataset.', '')
139
+ :name => ref.gsub(/^dataset\./, ''),
140
+ :dataset => ref.gsub(/^dataset\./, '')
141
141
  }
142
142
  else
143
143
  {
144
144
  :type => :date,
145
- :name => ref.gsub('dataset.', ''),
146
- :dataset => ref.gsub('dataset.', '')
145
+ :name => ref.gsub(/^dataset\./, ''),
146
+ :dataset => ref.gsub(/^dataset\./, '')
147
147
  }
148
148
  end
149
149
  end
@@ -8,11 +8,6 @@ module GoodData
8
8
  @json = json
9
9
  end
10
10
 
11
- def author
12
- data = client.get @json['invitation']['meta']['author']
13
- client.create GoodData::AccountSettings, data
14
- end
15
-
16
11
  def contributor
17
12
  data = client.get @json['invitation']['meta']['contributor']
18
13
  client.create GoodData::AccountSettings, data
@@ -28,67 +28,36 @@ module GoodData
28
28
  # @param obj [GoodData::User] Object to be modified
29
29
  # @param changes [Hash] Hash with modifications
30
30
  # @return [GoodData::User] Modified object
31
- def apply(obj, changes)
32
- changes.each do |param, val|
33
- next unless ASSIGNABLE_MEMBERS.include? param
34
- obj.send("#{param}=", val)
35
- end
36
- obj
37
- end
38
-
39
- # Gets hash representing diff of users
40
- #
41
- # @param user1 [GoodData::User] Original user
42
- # @param user2 [GoodData::User] User to compare with
43
- # @return [Hash] Hash representing diff
44
- def diff(user1, user2)
45
- res = {}
46
- ASSIGNABLE_MEMBERS.each do |k|
47
- l_value = user1.send("#{k}")
48
- r_value = user2.send("#{k}")
49
- res[k] = r_value if l_value != r_value
50
- end
51
- res
52
- end
53
-
54
- def diff_list(list1, list2)
55
- tmp = Hash[list1.map { |v| [v.email, v] }]
56
-
57
- res = {
58
- :added => [],
59
- :removed => [],
60
- :changed => [],
61
- :same => []
31
+ # def apply(obj, changes)
32
+ # changes.each do |param, val|
33
+ # next unless ASSIGNABLE_MEMBERS.include? param
34
+ # obj.send("#{param}=", val)
35
+ # end
36
+ # obj
37
+ # end
38
+ def create(data, options = { client: GoodData.connection })
39
+ c = client(options)
40
+ json = {
41
+ 'user' => {
42
+ 'content' => {
43
+ 'email' => data[:email] || data[:login],
44
+ 'login' => data[:login],
45
+ 'firstname' => data[:first_name],
46
+ 'lastname' => data[:last_name],
47
+ 'userRoles' => ['editor'],
48
+ 'password' => data[:password],
49
+ 'domain' => data[:domain],
50
+ # And following lines are even much more ugly hack
51
+ # 'authentication_modes' => ['sso', 'password']
52
+ },
53
+ 'meta' => {}
54
+ }
62
55
  }
56
+ c.create(self, json)
57
+ end
63
58
 
64
- list2.each do |user_new|
65
- user_existing = tmp[user_new.email]
66
- if user_existing.nil?
67
- res[:added] << user_new
68
- next
69
- end
70
-
71
- if user_existing != user_new
72
- diff = self.diff(user_existing, user_new)
73
- res[:changed] << {
74
- :user => user_existing,
75
- :diff => diff
76
- }
77
- else
78
- res[:same] << user_existing
79
- end
80
- end
81
-
82
- tmp = Hash[list2.map { |v| [v.email, v] }]
83
- list1.each do |user_existing|
84
- user_new = tmp[user_existing.email]
85
- if user_new.nil?
86
- res[:removed] << user_existing
87
- next
88
- end
89
- end
90
-
91
- res
59
+ def diff_list(list_1, list_2)
60
+ GoodData::Helpers.diff(list_1, list_2, key: :login)
92
61
  end
93
62
  end
94
63
 
@@ -101,13 +70,15 @@ module GoodData
101
70
  # @param right [GoodData::User] Project to compare with
102
71
  # @return [Boolean] True if same else false
103
72
  def ==(other)
104
- res = true
105
- ASSIGNABLE_MEMBERS.each do |k|
106
- l_val = send("#{k}")
107
- r_val = other.send("#{k}")
108
- res = false if l_val != r_val
109
- end
110
- res
73
+ return false unless other.respond_to?(:to_hash)
74
+ to_hash == other.to_hash
75
+ # res = true
76
+ # ASSIGNABLE_MEMBERS.each do |k|
77
+ # l_val = send("#{k}")
78
+ # r_val = other.send("#{k}")
79
+ # res = false if l_val != r_val
80
+ # end
81
+ # res
111
82
  end
112
83
 
113
84
  # Checks objects for non-equality
@@ -122,18 +93,9 @@ module GoodData
122
93
  #
123
94
  # @param changes [Hash] Hash with modifications
124
95
  # @return [GoodData::User] Modified object
125
- def apply(changes)
126
- GoodData::User.apply(self, changes)
127
- end
128
-
129
- # Gets author (person who created) of this object
130
- #
131
- # @return [String] Author
132
- def author
133
- url = @json['user']['meta']['author']
134
- data = client.get url
135
- client.factory.create(GoodData::Membership, data)
136
- end
96
+ # def apply(changes)
97
+ # GoodData::User.apply(self, changes)
98
+ # end
137
99
 
138
100
  # Gets the contributor
139
101
  #
@@ -151,6 +113,13 @@ module GoodData
151
113
  Time.parse(@json['user']['meta']['created'])
152
114
  end
153
115
 
116
+ # Is the member deleted?
117
+ #
118
+ # @return [Boolean] true if he is deleted
119
+ def deleted?
120
+ !(login =~ /^deleted-/).nil?
121
+ end
122
+
154
123
  # Gets hash representing diff of users
155
124
  #
156
125
  # @param user [GoodData::User] Another profile to compare with
@@ -275,11 +244,11 @@ module GoodData
275
244
  @json['user']['links']['self']
276
245
  end
277
246
 
278
- # Gets project which this membership relates to
279
- def project
280
- raw = client.get project_url
281
- client.factory.create(GoodData::Project, raw)
282
- end
247
+ # # Gets project which this membership relates to
248
+ # def project
249
+ # raw = client.get project_url
250
+ # client.factory.create(GoodData::Project, raw)
251
+ # end
283
252
 
284
253
  # Gets project id
285
254
  def project_id
@@ -304,15 +273,19 @@ module GoodData
304
273
  end
305
274
 
306
275
  # Gets first role
276
+ #
277
+ # @return [GoodData::ProjectRole] Array of project roles
307
278
  def role
308
- roles.first
279
+ roles && roles.first
309
280
  end
310
281
 
311
282
  # Gets the project roles of user
312
283
  #
313
284
  # @return [Array<GoodData::ProjectRole>] Array of project roles
314
285
  def roles
315
- tmp = client.get @json['user']['links']['roles']
286
+ roles_link = GoodData::Helpers.get_path(@json, %w(user links roles))
287
+ return unless roles_link
288
+ tmp = client.get roles_link
316
289
  tmp['associatedRoles']['roles'].pmap do |role_uri|
317
290
  role = client.get role_uri
318
291
  client.factory.create(GoodData::ProjectRole, role)
@@ -358,21 +331,75 @@ module GoodData
358
331
  #
359
332
  # @return [String] Object URI
360
333
  def uri
361
- @json['user']['links']['self']
334
+ links['self']
362
335
  end
363
336
 
364
337
  # Enables membership
365
338
  #
366
- # @return result from post execution
339
+ # @return [GoodData::Membership] returns self
367
340
  def enable
368
- self.status = 'enabled'
341
+ self.status = 'ENABLED'
342
+ self
343
+ end
344
+
345
+ # Is the member enabled?
346
+ #
347
+ # @return [Boolean] true if it is enabled
348
+ def enabled?
349
+ status == 'ENABLED'
369
350
  end
370
351
 
371
352
  # Disables membership
372
353
  #
373
- # @return result from post execution
354
+ # @return [GoodData::Membership] returns self
374
355
  def disable
375
- self.status = 'disabled'
356
+ self.status = 'DISABLED'
357
+ self
358
+ end
359
+
360
+ # Is the member enabled?
361
+ #
362
+ # @return [Boolean] true if it is disabled
363
+ def disabled?
364
+ !enabled?
365
+ end
366
+
367
+ def data
368
+ data = @json || {}
369
+ data['user'] || {}
370
+ end
371
+
372
+ def name
373
+ (first_name || '') + (last_name || '')
374
+ end
375
+
376
+ def meta
377
+ data['meta'] || {}
378
+ end
379
+
380
+ def links
381
+ data['links'] || {}
382
+ end
383
+
384
+ def content
385
+ data['content'] || {}
386
+ end
387
+
388
+ def to_hash
389
+ tmp = content.merge(meta).merge('uri' => uri).symbolize_keys
390
+ [
391
+ [:userRoles, :role],
392
+ [:companyName, :company_name],
393
+ [:phoneNumber, :phone_number],
394
+ [:firstname, :first_name],
395
+ [:lastname, :last_name],
396
+ [:authenticationModes, :authentication_modes]
397
+ ].each do |vals|
398
+ wire, rb = vals
399
+ tmp[rb] = tmp[wire]
400
+ tmp.delete(wire)
401
+ end
402
+ tmp
376
403
  end
377
404
 
378
405
  private
@@ -391,7 +418,10 @@ module GoodData
391
418
  }
392
419
  }
393
420
 
394
- @json = client.post("/gdc/projects/#{project_id}/users", payload)
421
+ res = client.post("/gdc/projects/#{project_id}/users", payload)
422
+ fail 'Update failed' unless res['projectUsersUpdateResult']['failed'].empty?
423
+ @json['user']['content']['status'] = new_status.to_s.upcase
424
+ self
395
425
  end
396
426
  end
397
427
  end
@@ -42,7 +42,6 @@ module GoodData
42
42
  if saved? # rubocop:disable Style/GuardClause
43
43
  client.delete(uri)
44
44
  meta.delete('uri')
45
- # ["uri"] = nil
46
45
  end
47
46
  end
48
47
 
@@ -68,7 +67,7 @@ module GoodData
68
67
  end
69
68
 
70
69
  def project
71
- @project ||= Project[uri.gsub(%r{\/obj\/\d+$}, ''), :client => client, :project => project]
70
+ @project ||= Project[uri.gsub(%r{\/obj\/\d+$}, ''), :client => client]
72
71
  end
73
72
 
74
73
  def saved?
@@ -41,6 +41,13 @@ module GoodData
41
41
  end
42
42
  alias_method :labels, :display_forms
43
43
 
44
+ def dimension
45
+ uri = content['dimension']
46
+ return nil if uri.nil?
47
+
48
+ GoodData::Dimension[uri, client: client, project: project]
49
+ end
50
+
44
51
  # Returns the first display form which is the primary one
45
52
  # @return [GoodData::Label] Primary label
46
53
  def primary_display_form
@@ -89,7 +89,7 @@ module GoodData
89
89
  tab = options[:tab] || ''
90
90
 
91
91
  req_uri = "/gdc/projects/#{GoodData.project.pid}/clientexport"
92
- x = GoodData.post(req_uri, 'clientExport' => { 'url' => "https://secure.gooddata.com/dashboard.html#project=#{GoodData.project.uri}&dashboard=#{uri}&tab=#{tab}&export=1", 'name' => title })
92
+ x = client.post(req_uri, 'clientExport' => { 'url' => "https://secure.gooddata.com/dashboard.html#project=#{GoodData.project.uri}&dashboard=#{uri}&tab=#{tab}&export=1", 'name' => title })
93
93
  GoodData.poll_on_code(x['asyncTask']['link']['poll'], process: false)
94
94
  end
95
95
 
@@ -0,0 +1,52 @@
1
+ # encoding: UTF-8
2
+
3
+ require_relative '../metadata'
4
+ require_relative '../../mixins/is_dimension'
5
+ require_relative 'metadata'
6
+
7
+ module GoodData
8
+ class Dimension < GoodData::MdObject
9
+ root_key :dimension
10
+
11
+ include GoodData::Mixin::IsDimension
12
+
13
+ class << self
14
+ # Method intended to get all objects of that type in a specified project
15
+ #
16
+ # @param options [Hash] the options hash
17
+ # @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
18
+ # @return [Array<GoodData::MdObject> | Array<Hash>] Return the appropriate metadata objects or their representation
19
+ def all(options = { :client => GoodData.connection, :project => GoodData.project })
20
+ query('dimensions', Dimension, options)
21
+ end
22
+
23
+ # Returns a Project object identified by given string
24
+ # The following identifiers are accepted
25
+ # - /gdc/md/<id>
26
+ # - /gdc/projects/<id>
27
+ # - <id>
28
+ #
29
+ def [](id, opts = { client: GoodData.connection })
30
+ return id if id.instance_of?(GoodData::Dimension) || id.respond_to?(:dimension?) && id.dimension?
31
+
32
+ if id == :all
33
+ Dimension.all({ client: GoodData.connection }.merge(opts))
34
+ else
35
+ uri = id
36
+
37
+ c = client(opts)
38
+ fail ArgumentError, 'No :client specified' if c.nil?
39
+
40
+ response = c.get(uri)
41
+ c.factory.create(Dimension, response)
42
+ end
43
+ end
44
+ end
45
+
46
+ def attributes
47
+ content['attributes'].map do |attribute|
48
+ client.create(Attribute, { 'attribute' => attribute }, project: project)
49
+ end
50
+ end
51
+ end
52
+ end
@@ -13,7 +13,7 @@ module GoodData
13
13
  include GoodData::Mixin::IsFact
14
14
 
15
15
  # TODO: verify that we have all (which we do not right now)
16
- FACT_BASE_AGGREGATIONS = [:sum, :min, :max, :avg, :median]
16
+ FACT_BASE_AGGREGATIONS = [:sum, :min, :max, :avg, :median, :runsum, :runmin, :runmax, :runavg, :runstdev, :runstdevp, :runvar, :runvarp, :stdev, :stdevp, :var, :varp]
17
17
 
18
18
  class << self
19
19
  # Method intended to get all objects of that type in a specified project
@@ -55,14 +55,28 @@ module GoodData
55
55
  # @return [Array]
56
56
  def values(options = {})
57
57
  limit = options[:limit] || 100
58
- results = client.post("#{uri}/validElements?limit=#{limit}&offset=0&order=asc", {})
59
- results['validElements']['items'].map do |el|
60
- v = el['element']
61
- {
62
- :value => v['title'],
63
- :uri => v['uri']
64
- }
58
+ offset = 0
59
+ vals = []
60
+ loop do
61
+ results = GoodData.post("#{uri}/validElements?limit=#{limit}&offset=#{offset}&order=asc", {})
62
+ x = results['validElements']['items'].map do |el|
63
+ v = el['element']
64
+ {
65
+ :value => v['title'],
66
+ :uri => v['uri']
67
+ }
68
+ end
69
+ vals.concat(x)
70
+ break if vals.length < offset
71
+ offset += limit
65
72
  end
73
+ vals
74
+ end
75
+
76
+ def values_count(options = {})
77
+ limit = options[:limit] || 100
78
+ results = client.post("#{uri}/validElements?limit=#{limit}&offset=0&order=asc", {})
79
+ results['validElements']['paging']['total'].to_i
66
80
  end
67
81
 
68
82
  # Gives an attribute of current label