gooddata 0.6.3 → 0.6.4

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 (70) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +3 -1
  3. data/CHANGELOG.markdown +6 -0
  4. data/README.md +1 -0
  5. data/gooddata.gemspec +2 -1
  6. data/lib/gooddata.rb +4 -1
  7. data/lib/gooddata/bricks/base_downloader.rb +33 -19
  8. data/lib/gooddata/bricks/middleware/bulk_salesforce_middleware.rb +49 -25
  9. data/lib/gooddata/bricks/middleware/restforce_middleware.rb +36 -33
  10. data/lib/gooddata/cli/commands/project_cmd.rb +6 -4
  11. data/lib/gooddata/client.rb +1 -1
  12. data/lib/gooddata/commands/api.rb +1 -1
  13. data/lib/gooddata/commands/auth.rb +1 -1
  14. data/lib/gooddata/connection.rb +13 -10
  15. data/lib/gooddata/core/connection.rb +1 -1
  16. data/lib/gooddata/core/user.rb +11 -3
  17. data/lib/gooddata/exceptions/validation_error.rb +12 -0
  18. data/lib/gooddata/extensions/extensions.rb +6 -0
  19. data/lib/gooddata/goodzilla/goodzilla.rb +2 -2
  20. data/lib/gooddata/helpers/csv_helper.rb +57 -0
  21. data/lib/gooddata/{helpers.rb → helpers/global_helpers.rb} +0 -0
  22. data/lib/gooddata/helpers/helpers.rb +6 -0
  23. data/lib/gooddata/models/domain.rb +134 -24
  24. data/lib/gooddata/models/membership.rb +402 -0
  25. data/lib/gooddata/models/metadata.rb +64 -7
  26. data/lib/gooddata/models/metadata/attribute.rb +27 -12
  27. data/lib/gooddata/models/metadata/column.rb +1 -1
  28. data/lib/gooddata/models/metadata/dashboard.rb +7 -6
  29. data/lib/gooddata/models/metadata/display_form.rb +17 -2
  30. data/lib/gooddata/models/metadata/fact.rb +13 -7
  31. data/lib/gooddata/models/metadata/metric.rb +9 -9
  32. data/lib/gooddata/models/metadata/report.rb +7 -8
  33. data/lib/gooddata/models/metadata/report_definition.rb +10 -11
  34. data/lib/gooddata/models/metadata/schema.rb +1 -1
  35. data/lib/gooddata/models/model.rb +1 -1
  36. data/lib/gooddata/models/process.rb +44 -25
  37. data/lib/gooddata/models/profile.rb +365 -13
  38. data/lib/gooddata/models/project.rb +245 -35
  39. data/lib/gooddata/models/project_blueprint.rb +42 -18
  40. data/lib/gooddata/models/project_creator.rb +4 -1
  41. data/lib/gooddata/models/project_role.rb +7 -7
  42. data/lib/gooddata/models/schedule.rb +17 -1
  43. data/lib/gooddata/models/schema_blueprint.rb +19 -2
  44. data/lib/gooddata/version.rb +1 -1
  45. data/out.txt +0 -0
  46. data/spec/data/users.csv +12 -0
  47. data/spec/helpers/connection_helper.rb +1 -0
  48. data/spec/helpers/csv_helper.rb +12 -0
  49. data/spec/helpers/project_helper.rb +1 -1
  50. data/spec/integration/full_project_spec.rb +136 -3
  51. data/spec/spec_helper.rb +9 -0
  52. data/spec/unit/commands/command_user_spec.rb +1 -1
  53. data/spec/unit/extensions/hash_spec.rb +19 -0
  54. data/spec/unit/godzilla/goodzilla_spec.rb +15 -0
  55. data/spec/unit/helpers/csv_helper_spec.rb +18 -0
  56. data/spec/unit/models/domain_spec.rb +47 -4
  57. data/spec/unit/models/md_object_spec.rb +8 -0
  58. data/spec/unit/models/membership_spec.rb +128 -0
  59. data/spec/unit/models/metadata_spec.rb +38 -0
  60. data/spec/unit/models/profile_spec.rb +212 -0
  61. data/spec/unit/models/project_blueprint_spec.rb +35 -8
  62. data/spec/unit/models/project_role_spec.rb +6 -6
  63. data/spec/unit/models/project_spec.rb +226 -13
  64. data/spec/unit/models/schedule_spec.rb +58 -0
  65. data/tmp/.gitkeepme +0 -0
  66. metadata +36 -11
  67. data/lib/gooddata/models/account_settings.rb +0 -124
  68. data/lib/gooddata/models/user.rb +0 -165
  69. data/spec/unit/models/account_settings_spec.rb +0 -28
  70. data/spec/unit/models/user_spec.rb +0 -16
@@ -1,32 +1,384 @@
1
1
  # encoding: UTF-8
2
2
 
3
+ require_relative 'project'
4
+
3
5
  module GoodData
6
+ # Account settings representation with some added sugar
4
7
  class Profile
5
- private_class_method :new
6
- attr_reader :user, :json
8
+ attr_reader :json
9
+
10
+ EMPTY_OBJECT = {
11
+ 'accountSetting' => {
12
+ 'companyName' => nil,
13
+ 'country' => nil,
14
+ 'created' => nil,
15
+ 'firstName' => nil,
16
+ 'lastName' => nil,
17
+ 'login' => nil,
18
+ 'phoneNumber' => nil,
19
+ 'position' => nil,
20
+ 'timezone' => nil,
21
+ 'updated' => nil,
22
+ 'links' => {
23
+ 'projects' => nil,
24
+ 'self' => nil
25
+ },
26
+ 'email' => nil,
27
+ 'authenticationModes' => []
28
+ }
29
+ }
30
+
31
+ ASSIGNABLE_MEMBERS = [
32
+ :company,
33
+ :country,
34
+ :email,
35
+ :login,
36
+ :first_name,
37
+ :last_name,
38
+ :phone,
39
+ :position,
40
+ :timezone
41
+ ]
7
42
 
8
43
  class << self
9
- def load
10
- # GoodData.logger.info "Loading user profile..."
11
- Profile.send 'new'
44
+ # Apply changes to object.
45
+ #
46
+ # @param obj [GoodData::Profile] Object to be modified
47
+ # @param changes [Hash] Hash with modifications
48
+ # @return [GoodData::Profile] Modified object
49
+ def apply(obj, changes)
50
+ changes.each do |param, val|
51
+ next unless ASSIGNABLE_MEMBERS.include? param
52
+ obj.send("#{param}=", val)
53
+ end
54
+ obj
55
+ end
56
+
57
+ # Creates new instance from hash with attributes
58
+ #
59
+ # @param attributes [Hash] Hash with initial attributes
60
+ # @return [GoodData::Profile] New profile instance
61
+ def create(attributes)
62
+ json = EMPTY_OBJECT.dup
63
+ res = GoodData::Profile.new(json)
64
+
65
+ attributes.each do |k, v|
66
+ res.send("#{k}=", v) if ASSIGNABLE_MEMBERS.include? k
67
+ end
68
+
69
+ res.save!
70
+ res
71
+ end
72
+
73
+ # Gets user currently logged in
74
+ # @return [GoodData::Profile] User currently logged-in
75
+ def current
76
+ json = GoodData.get GoodData.connection.user['profile']
77
+ GoodData::Profile.new(json)
78
+ end
79
+
80
+ # Gets hash representing diff of profiles
81
+ #
82
+ # @param user1 [GoodData::Profile] Original user
83
+ # @param user2 [GoodData::Profile] User to compare with
84
+ # @return [Hash] Hash representing diff
85
+ def diff(user1, user2)
86
+ res = {}
87
+ ASSIGNABLE_MEMBERS.each do |k|
88
+ l_value = user1.send("#{k}")
89
+ r_value = user2.send("#{k}")
90
+ res[k] = r_value if l_value != r_value
91
+ end
92
+ res
93
+ end
94
+
95
+ def diff_list(list1, list2)
96
+ tmp = Hash[list1.map { |v| [v.email, v] }]
97
+
98
+ res = {
99
+ :added => [],
100
+ :removed => [],
101
+ :changed => []
102
+ }
103
+
104
+ list2.each do |user_new|
105
+ user_existing = tmp[user_new.email]
106
+ if user_existing.nil?
107
+ res[:added] << user_new
108
+ next
109
+ end
110
+
111
+ if user_existing != user_new
112
+ diff = self.diff(user_existing, user_new)
113
+ res[:changed] << {
114
+ :user => user_existing,
115
+ :diff => diff
116
+ }
117
+ end
118
+ end
119
+
120
+ tmp = Hash[list2.map { |v| [v.email, v] }]
121
+ list1.each do |user_existing|
122
+ user_new = tmp[user_existing.email]
123
+ if user_new.nil?
124
+ res[:removed] << user_existing
125
+ next
126
+ end
127
+ end
128
+
129
+ res
130
+ end
131
+ end
132
+
133
+ # Creates new instance
134
+ #
135
+ # @return [Profile] New Profile instance
136
+ def initialize(json)
137
+ @json = json
138
+ @dirty = false
139
+ end
140
+
141
+ # Checks objects for equality
142
+ #
143
+ # @param right [GoodData::Profile] Project to compare with
144
+ # @return [Boolean] True if same else false
145
+ def ==(other)
146
+ res = true
147
+ ASSIGNABLE_MEMBERS.each do |k|
148
+ l_val = send("#{k}")
149
+ r_val = other.send("#{k}")
150
+ res = false if l_val != r_val
12
151
  end
152
+ res
153
+ end
154
+
155
+ # Checks objects for non-equality
156
+ #
157
+ # @param right [GoodData::Profile] Project to compare with
158
+ # @return [Boolean] True if different else false
159
+ def !=(other)
160
+ !(self == other)
161
+ end
162
+
163
+ # Apply changes to object.
164
+ #
165
+ # @param changes [Hash] Hash with modifications
166
+ # @return [GoodData::Profile] Modified object
167
+ def apply(changes)
168
+ GoodData::Profile.apply(self, changes)
169
+ end
170
+
171
+ # Gets the company name
172
+ #
173
+ # @return [String] Company name
174
+ def company
175
+ @json['accountSetting']['companyName'] || ''
176
+ end
177
+
178
+ # Set the company name
179
+ #
180
+ # @param val [String] Company name to be set
181
+ def company=(val)
182
+ @dirty ||= company != val
183
+ @json['accountSetting']['companyName'] = val
184
+ end
185
+
186
+ # Gets the country
187
+ #
188
+ # @return [String] Country
189
+ def country
190
+ @json['accountSetting']['country'] || ''
191
+ end
192
+
193
+ # Set the country
194
+ #
195
+ # @param val [String] Country to be set
196
+ def country=(val)
197
+ @dirty ||= country != val
198
+ @json['accountSetting']['country'] = val
13
199
  end
14
200
 
201
+ # Gets date when created
202
+ #
203
+ # @return [DateTime] Created date
204
+ def created
205
+ DateTime.parse(@json['accountSetting']['created'])
206
+ end
207
+
208
+ # Deletes this account settings
209
+ def delete
210
+ GoodData.delete uri
211
+ end
212
+
213
+ # Gets hash representing diff of profiles
214
+ #
215
+ # @param user [GoodData::Profile] Another profile to compare with
216
+ # @return [Hash] Hash representing diff
217
+ def diff(user)
218
+ GoodData::Profile.diff(self, user)
219
+ end
220
+
221
+ # Gets the email
222
+ #
223
+ # @return [String] Email address
224
+ def email
225
+ @json['accountSetting']['email'] || ''
226
+ end
227
+
228
+ # Set the email
229
+ #
230
+ # @param val [String] Email to be set
231
+ def email=(val)
232
+ @dirty ||= email != val
233
+ @json['accountSetting']['email'] = val
234
+ end
235
+
236
+ # Gets the first name
237
+ #
238
+ # @return [String] First name
239
+ def first_name
240
+ @json['accountSetting']['firstName'] || ''
241
+ end
242
+
243
+ # Set the first name
244
+ #
245
+ # @param val [String] First name to be set
246
+ def first_name=(val)
247
+ @dirty ||= first_name != val
248
+ @json['accountSetting']['firstName'] = val
249
+ end
250
+
251
+ # Gets the last name
252
+ #
253
+ # @return [String] Last name
254
+ def last_name
255
+ @json['accountSetting']['lastName'] || ''
256
+ end
257
+
258
+ # Set the last name
259
+ #
260
+ # @param val [String] Last name to be set
261
+ def last_name=(val)
262
+ @dirty ||= last_name != val
263
+ @json['accountSetting']['lastName'] = val
264
+ end
265
+
266
+ # Gets the login
267
+ #
268
+ # @return [String] Login
269
+ def login
270
+ @json['accountSetting']['login'] || ''
271
+ end
272
+
273
+ # Set the login
274
+ #
275
+ # @param val [String] Login to be set
276
+ def login=(val)
277
+ @dirty ||= login != val
278
+ @json['accountSetting']['login'] = val
279
+ end
280
+
281
+ # Get full name
282
+ #
283
+ # @return [String] Full name
284
+ def name
285
+ "#{first_name} #{last_name}"
286
+ end
287
+
288
+ # Gets the resource identifier
289
+ #
290
+ # @return [String] Resource identifier
291
+ def obj_id
292
+ uri.split('/').last
293
+ end
294
+
295
+ alias_method :account_setting_id, :obj_id
296
+
297
+ # Gets the phone
298
+ #
299
+ # @return [String] Phone
300
+ def phone
301
+ @json['accountSetting']['phone'] || ''
302
+ end
303
+
304
+ # Set the phone
305
+ #
306
+ # @param val [String] Phone to be set
307
+ def phone=(val)
308
+ @dirty ||= phone != val
309
+ @json['accountSetting']['phone'] = val
310
+ end
311
+
312
+ # Gets the position in company
313
+ #
314
+ # @return [String] Position in company
315
+ def position
316
+ @json['accountSetting']['position'] || ''
317
+ end
318
+
319
+ # Set the position
320
+ #
321
+ # @param val [String] Position to be set
322
+ def position=(val)
323
+ @dirty ||= position != val
324
+ @json['accountSetting']['position'] = val
325
+ end
326
+
327
+ # Gets the array of projects
328
+ #
329
+ # @return [Array<GoodData::Project>] Array of project where account settings belongs to
15
330
  def projects
16
- @json['accountSetting']['links']['projects']
331
+ res = []
332
+
333
+ projects = GoodData.get @json['accountSetting']['links']['projects']
334
+ projects['projects'].each do |project|
335
+ res << GoodData::Project.new(project)
336
+ end
337
+
338
+ res
17
339
  end
18
340
 
19
- alias_method :to_json, :json
341
+ # Saves object if dirty, clears dirty flag
342
+ def save!
343
+ if @dirty
344
+ raw = @json.dup
345
+ raw['accountSetting'].delete('login')
20
346
 
21
- def [](key, options = {})
22
- @json['accountSetting'][key]
347
+ if uri && !uri.empty?
348
+ url = "/gdc/account/profile/#{obj_id}"
349
+ @json = GoodData.put url, raw
350
+ @dirty = false
351
+ end
352
+ end
23
353
  end
24
354
 
25
- private
355
+ # Gets the preferred timezone
356
+ #
357
+ # @return [String] Preferred timezone
358
+ def timezone
359
+ @json['accountSetting']['timezone'] || ''
360
+ end
361
+
362
+ # Set the timezone
363
+ #
364
+ # @param val [String] Timezone to be set
365
+ def timezone=(val)
366
+ @dirty ||= timezone != val
367
+ @json['accountSetting']['timezone'] = val
368
+ end
369
+
370
+ # Gets the date when updated
371
+ #
372
+ # @return [DateTime] Updated date
373
+ def updated
374
+ DateTime.parse(@json['accountSetting']['updated'])
375
+ end
26
376
 
27
- def initialize
28
- @json = GoodData.get GoodData.connection.user['profile']
29
- @user = @json['accountSetting']['firstName'] + ' ' + @json['accountSetting']['lastName']
377
+ # Gets the resource REST URI
378
+ #
379
+ # @return [String] Resource URI
380
+ def uri
381
+ @json['accountSetting']['links']['self']
30
382
  end
31
383
  end
32
384
  end
@@ -1,5 +1,6 @@
1
1
  # encoding: UTF-8
2
2
 
3
+ require 'csv'
3
4
  require 'zip'
4
5
  require 'fileutils'
5
6
 
@@ -21,10 +22,7 @@ module GoodData
21
22
  # Returns an array of all projects accessible by
22
23
  # current user
23
24
  def all
24
- json = GoodData.get GoodData.profile.projects
25
- json['projects'].map do |project|
26
- Project.new project
27
- end
25
+ GoodData.profile.projects
28
26
  end
29
27
 
30
28
  # Returns a Project object identified by given string
@@ -74,7 +72,7 @@ module GoodData
74
72
  'driver' => attributes[:driver] || 'Pg'
75
73
  }
76
74
  }
77
- }
75
+ }
78
76
 
79
77
  json['project']['meta']['projectTemplate'] = attributes[:template] if attributes[:template] && !attributes[:template].empty?
80
78
  project = Project.new json
@@ -98,6 +96,23 @@ module GoodData
98
96
  sleep 3
99
97
  project
100
98
  end
99
+
100
+ # Takes one CSV line and creates hash from data extracted
101
+ #
102
+ # @param row CSV row
103
+ def user_csv_import(row)
104
+ {
105
+ 'user' => {
106
+ 'content' => {
107
+ 'email' => row[0],
108
+ 'login' => row[1],
109
+ 'firstname' => row[2],
110
+ 'lastname' => row[3]
111
+ },
112
+ 'meta' => {}
113
+ }
114
+ }
115
+ end
101
116
  end
102
117
 
103
118
  # Creates a data set within the project
@@ -139,13 +154,6 @@ module GoodData
139
154
  @json['project']['meta']['author']
140
155
  end
141
156
 
142
- # Adds user to project
143
- #
144
- # TODO: Discuss with @fluke777 if is not #invite sufficient
145
- def add_user(email_address, domain)
146
- fail 'Not implemented'
147
- end
148
-
149
157
  # Returns web interface URI of project
150
158
  #
151
159
  # @return [String] Project URL
@@ -218,7 +226,7 @@ module GoodData
218
226
  #
219
227
  # @return [DateTime] Date time when created
220
228
  def created
221
- DateTime.parse(@json['meta']['created'])
229
+ DateTime.parse(@json['project']['meta']['created'])
222
230
  end
223
231
 
224
232
  # Gets ruby wrapped raw project JSON data
@@ -234,6 +242,13 @@ module GoodData
234
242
  end
235
243
  end
236
244
 
245
+ # Gets processes for the project
246
+ #
247
+ # @return [Array<GoodData::Process>] Processes for the current project
248
+ def processes
249
+ GoodData::Process.all
250
+ end
251
+
237
252
  # Deletes project
238
253
  def delete
239
254
  fail "Project '#{title}' with id #{uri} is already deleted" if state == :deleted
@@ -249,10 +264,11 @@ module GoodData
249
264
  #
250
265
  # @param [String] role_name Title of role to look for
251
266
  # @return [GoodData::ProjectRole] Project role if found
252
- def get_role_by_identifier(role_name)
253
- tmp = roles
254
- tmp.each do |role|
255
- return role if role.identifier.downcase == role_name.downcase
267
+ def get_role_by_identifier(role_name, role_list = roles)
268
+ role_name = role_name.downcase.gsub(/role$/, '')
269
+ role_list.each do |role|
270
+ tmp_role_name = role.identifier.downcase.gsub(/role$/, '')
271
+ return role if tmp_role_name == role_name
256
272
  end
257
273
  nil
258
274
  end
@@ -261,9 +277,8 @@ module GoodData
261
277
  #
262
278
  # @param [String] role_summary Summary of role to look for
263
279
  # @return [GoodData::ProjectRole] Project role if found
264
- def get_role_by_summary(role_summary)
265
- tmp = roles
266
- tmp.each do |role|
280
+ def get_role_by_summary(role_summary, role_list = roles)
281
+ role_list.each do |role|
267
282
  return role if role.summary.downcase == role_summary.downcase
268
283
  end
269
284
  nil
@@ -273,14 +288,47 @@ module GoodData
273
288
  #
274
289
  # @param [String] role_title Title of role to look for
275
290
  # @return [GoodData::ProjectRole] Project role if found
276
- def get_role_by_title(role_title)
277
- tmp = roles
278
- tmp.each do |role|
291
+ def get_role_by_title(role_title, role_list = roles)
292
+ role_list.each do |role|
279
293
  return role if role.title.downcase == role_title.downcase
280
294
  end
281
295
  nil
282
296
  end
283
297
 
298
+ # Gets project role
299
+ #
300
+ # @param [String] role_title Title of role to look for
301
+ # @return [GoodData::ProjectRole] Project role if found
302
+ def get_role(role_name, role_list = roles)
303
+ return role_name if role_name.is_a? GoodData::ProjectRole
304
+
305
+ role_name.downcase!
306
+ role_list.each do |role|
307
+ return role if role.uri == role_name ||
308
+ role.identifier.downcase == role_name ||
309
+ role.identifier.downcase.gsub(/role$/, '') == role_name ||
310
+ role.title.downcase == role_name ||
311
+ role.summary.downcase == role_name
312
+ end
313
+ nil
314
+ end
315
+
316
+ # Gets user by its email, full_name, login or uri
317
+ #
318
+ # @param [String] name Name to look for
319
+ # @param [Array<GoodData::User>]user_list Optional cached list of users used for look-ups
320
+ # @return [GoodDta::Membership] User
321
+ def get_user(name, user_list = users)
322
+ return name if name.instance_of?(GoodData::Membership)
323
+ name.downcase!
324
+ user_list.each do |user|
325
+ return user if user.uri.downcase == name ||
326
+ user.login.downcase == name ||
327
+ user.email.downcase == name
328
+ end
329
+ nil
330
+ end
331
+
284
332
  # Initializes object instance from raw wire JSON
285
333
  #
286
334
  # @param json Json used for initialization
@@ -300,9 +348,8 @@ module GoodData
300
348
 
301
349
  role_url = nil
302
350
  if role.index('/gdc/') != 0
303
- tmp = get_role_by_identifier(role)
304
- tmp = get_role_by_title(role) if tmp.nil?
305
- role_url = tmp['url'] if tmp
351
+ tmp = get_role(role)
352
+ role_url = tmp.uri if tmp
306
353
  else
307
354
  role_url = role if role_url.nil?
308
355
  end
@@ -352,6 +399,29 @@ module GoodData
352
399
  @md ||= Links.new GoodData.get(data['links']['metadata'])
353
400
  end
354
401
 
402
+ # Gets membership for profile specified
403
+ #
404
+ # @param [GoodData::Profile] profile - Profile to be checked
405
+ # @param [Array<GoodData::Membership>] list Optional list of members to check against
406
+ # @return [GoodData::Membership] Membership if found
407
+ def member(profile, list = members)
408
+ if profile.is_a? String
409
+ return list.find do |m|
410
+ m.uri == profile || m.login == profile
411
+ end
412
+ end
413
+ list.find { |m| m.login == profile.login }
414
+ end
415
+
416
+ # Checks if the profile is member of project
417
+ #
418
+ # @param [GoodData::Profile] profile - Profile to be checked
419
+ # @param [Array<GoodData::Membership>] list Optional list of members to check against
420
+ # @return [Boolean] true if is member else false
421
+ def member?(profile, list = members)
422
+ !member(profile, list).nil?
423
+ end
424
+
355
425
  # Gets raw resource ID
356
426
  #
357
427
  # @return [String] Raw resource ID
@@ -422,16 +492,11 @@ module GoodData
422
492
  # @return [Array<GoodData::ProjectRole>] List of roles
423
493
  def roles
424
494
  url = "/gdc/projects/#{pid}/roles"
425
-
426
- res = []
427
-
428
495
  tmp = GoodData.get(url)
429
- tmp['projectRoles']['roles'].each do |role_url|
496
+ tmp['projectRoles']['roles'].map do |role_url|
430
497
  json = GoodData.get role_url
431
- res << GoodData::ProjectRole.new(json)
498
+ GoodData::ProjectRole.new(json)
432
499
  end
433
-
434
- res
435
500
  end
436
501
 
437
502
  # Saves project
@@ -498,7 +563,7 @@ module GoodData
498
563
  #
499
564
  # @return [DateTime] Date time of last update
500
565
  def updated
501
- DateTime.parse(@json['meta']['updated'])
566
+ DateTime.parse(@json['project']['meta']['updated'])
502
567
  end
503
568
 
504
569
  # Uploads file to project
@@ -521,12 +586,157 @@ module GoodData
521
586
 
522
587
  tmp = GoodData.get @json['project']['links']['users']
523
588
  tmp['users'].map do |user|
524
- res << GoodData::User.new(user)
589
+ res << GoodData::Membership.new(user)
525
590
  end
526
591
 
527
592
  res
528
593
  end
529
594
 
595
+ alias_method :members, :users
596
+
597
+ def users_create(list, role_list = roles)
598
+ domains = {}
599
+ list.map do |user|
600
+ # TODO: Add user here
601
+ domain_name = user.json['user']['content']['domain']
602
+
603
+ # Lookup for domain in cache'
604
+ domain = domains[domain_name]
605
+
606
+ # Get domain info from REST, add to cache
607
+ if domain.nil?
608
+ domain = {
609
+ :domain => GoodData::Domain[domain_name],
610
+ :users => GoodData::Domain[domain_name].users
611
+ }
612
+
613
+ domain[:users_map] = Hash[domain[:users].map { |u| [u.email, u] }]
614
+ domains[domain_name] = domain
615
+ end
616
+
617
+ # Check if user exists in domain
618
+ domain_user = domain[:users_map][user.email]
619
+ fail ArgumentError, "Trying to add user '#{user.login}' which is not valid user in domain '#{domain_name}'" if domain_user.nil?
620
+
621
+ # Lookup for role
622
+ role_name = user.json['user']['content']['role'] || 'readOnlyUser'
623
+ role = get_role(role_name, role_list)
624
+ fail ArgumentError, "Invalid role name specified '#{role_name}' for user '#{user.email}'" if role.nil?
625
+
626
+ # Assign user project role
627
+ set_user_roles(domain_user, [role.uri], role_list)
628
+ end
629
+ end
630
+
631
+ # Imports users from CSV
632
+ #
633
+ # # Features
634
+ # - Create new users
635
+ # - Delete old users
636
+ # - Update existing users
637
+ #
638
+ # CSV Format
639
+ # TODO: Describe CSV Format here
640
+ #
641
+ # @param path CSV file to be loaded
642
+ # @param opts Optional additional options
643
+ def users_import(new_users, domain = nil)
644
+ # Diff users
645
+ diff = GoodData::Membership.diff_list(users, new_users)
646
+
647
+ # Create domain users
648
+ GoodData::Domain.users_create(diff[:added], domain)
649
+
650
+ # Create new users
651
+ role_list = roles
652
+ users_create(diff[:added], role_list)
653
+
654
+ # Get changed users objects from hash
655
+ list = diff[:changed].map do |user|
656
+ user[:user]
657
+ end
658
+
659
+ # Join list of changed users with 'same' users
660
+ list = list.zip(diff[:same]).flatten.compact
661
+
662
+ new_users_map = Hash[new_users.map { |u| [u.email, u] }]
663
+
664
+ # Create list with user, desired_roles hashes
665
+ list = list.map do |user|
666
+ {
667
+ :user => user,
668
+ :roles => new_users_map[user.email].json['user']['content']['role'].split(' ').map { |r| r.downcase }.sort
669
+ }
670
+ end
671
+
672
+ # Update existing users
673
+ set_users_roles(list, role_list)
674
+
675
+ # Remove old users
676
+ users_remove(diff[:removed])
677
+ end
678
+
679
+ # Disable users
680
+ #
681
+ # @param list List of users to be disabled
682
+ def users_remove(list)
683
+ list.map do |user|
684
+ user.disable
685
+ end
686
+ end
687
+
688
+ # Update user
689
+ #
690
+ # @param user User to be updated
691
+ # @param desired_roles Roles to be assigned to user
692
+ # @param role_list Optional cached list of roles used for lookups
693
+ def set_user_roles(user, desired_roles, role_list = roles)
694
+ if user.is_a? String
695
+ user = get_user(user)
696
+ fail ArgumentError, "Invalid user '#{user}' specified" if user.nil?
697
+ end
698
+
699
+ desired_roles = [desired_roles] unless desired_roles.is_a? Array
700
+
701
+ roles = desired_roles.map do |role_name|
702
+ role = get_role(role_name, role_list)
703
+ fail ArgumentError, "Invalid role '#{role_name}' specified for user '#{user.email}'" if role.nil?
704
+ role.uri
705
+ end
706
+
707
+ url = "#{uri}/users"
708
+ payload = {
709
+ 'user' => {
710
+ 'content' => {
711
+ 'status' => 'ENABLED',
712
+ 'userRoles' => roles
713
+ },
714
+ 'links' => {
715
+ 'self' => user.uri
716
+ }
717
+ }
718
+ }
719
+
720
+ GoodData.post url, payload
721
+ end
722
+
723
+ alias_method :add_user, :set_user_roles
724
+
725
+ # Update list of users
726
+ #
727
+ # @param list List of users to be updated
728
+ # @param role_list Optional list of cached roles to prevent unnecessary server round-trips
729
+ def set_users_roles(list, role_list = roles)
730
+ list.map do |user_hash|
731
+ user = user_hash[:user]
732
+ roles = user_hash[:role] || user_hash[:roles]
733
+ {
734
+ :user => user,
735
+ :result => set_user_roles(user, roles, role_list)
736
+ }
737
+ end
738
+ end
739
+
530
740
  # Run validation on project
531
741
  # Valid settins for validation are (default all):
532
742
  # ldm - Checks the consistency of LDM objects.