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,5 +1,4 @@
1
1
  # encoding: UTF-8
2
-
3
2
  require 'gooddata'
4
3
 
5
4
  describe GoodData::Model::ProjectBlueprint do
@@ -20,19 +19,34 @@ describe GoodData::Model::ProjectBlueprint do
20
19
  end
21
20
 
22
21
  it 'valid blueprint should be marked as valid' do
23
- @blueprint.model_valid?.should == true
22
+ @blueprint.valid?.should == true
24
23
  end
25
24
 
26
25
  it 'valid blueprint should give you empty array of errors' do
27
- expect(@blueprint.validate_model).to be_empty
26
+ expect(@blueprint.validate).to be_empty
27
+ end
28
+
29
+ it 'model should be invalid if it contains more than one anchor' do
30
+ builder = GoodData::Model::ProjectBuilder.create("my_bp") do |p|
31
+ p.add_dataset("repos") do |d|
32
+ d.add_anchor("repo_id")
33
+ d.add_anchor("repo_id2")
34
+ d.add_attribute("name")
35
+ end
36
+ end
37
+ bp = GoodData::Model::ProjectBlueprint.new(builder)
38
+ bp.valid?.should == false
39
+ errors = bp.validate
40
+ errors.first.should == {:anchor => 2}
41
+ errors.count.should == 1
28
42
  end
29
43
 
30
44
  it 'invalid blueprint should be marked as invalid' do
31
- @invalid_blueprint.model_valid?.should == false
45
+ @invalid_blueprint.valid?.should == false
32
46
  end
33
47
 
34
48
  it 'invalid blueprint should give you list of violating references' do
35
- errors = @invalid_blueprint.validate_model
49
+ errors = @invalid_blueprint.validate
36
50
  errors.size.should == 1
37
51
  errors.first.should == {
38
52
  type: 'reference',
@@ -47,6 +61,19 @@ describe GoodData::Model::ProjectBlueprint do
47
61
  expect(refs).to be_empty
48
62
  end
49
63
 
64
+ it "should be possible to create from ProjectBlueprint from ProjectBuilder in several ways" do
65
+ builder = GoodData::Model::SchemaBuilder.new("stuff") do |d|
66
+ d.add_attribute("id", :title => "My Id")
67
+ d.add_fact("amount", :title => "Amount")
68
+ end
69
+
70
+ bp1 = GoodData::Model::ProjectBlueprint.new(builder)
71
+ bp1.valid?.should == true
72
+
73
+ bp2 = builder.to_blueprint
74
+ bp2.valid?.should == true
75
+ end
76
+
50
77
  it 'should be able to get dataset by name' do
51
78
  ds = @blueprint.find_dataset('devs')
52
79
  ds.name.should == 'devs'
@@ -107,9 +134,9 @@ describe GoodData::Model::ProjectBlueprint do
107
134
  d.add_label("name", :reference => "invalid_ref")
108
135
  end
109
136
  end
110
- bp = GoodData::Model::ProjectBlueprint.from_json(builder.to_hash)
111
- bp.model_valid?.should == false
112
- errors = bp.validate_model
137
+ bp = GoodData::Model::ProjectBlueprint.new(builder)
138
+ bp.valid?.should == false
139
+ errors = bp.validate
113
140
  errors.count.should == 1
114
141
  end
115
142
 
@@ -16,16 +16,16 @@ describe GoodData::ProjectRole do
16
16
  end
17
17
 
18
18
  describe '#author' do
19
- it 'Returns author as GoodData::AccountSettings' do
19
+ it 'Returns author as GoodData::Profile' do
20
20
  res = @role.author
21
- expect(res).to be_an_instance_of(GoodData::AccountSettings)
21
+ expect(res).to be_an_instance_of(GoodData::Profile)
22
22
  end
23
23
  end
24
24
 
25
25
  describe '#contributor' do
26
- it 'Returns contributor as GoodData::AccountSettings' do
26
+ it 'Returns contributor as GoodData::Profile' do
27
27
  res = @role.contributor
28
- expect(res).to be_an_instance_of(GoodData::AccountSettings)
28
+ expect(res).to be_an_instance_of(GoodData::Profile)
29
29
  end
30
30
  end
31
31
 
@@ -79,11 +79,11 @@ describe GoodData::ProjectRole do
79
79
  end
80
80
 
81
81
  describe '#users' do
82
- it 'Returns users as Array<GoodData::AccountSettings>' do
82
+ it 'Returns users as Array<GoodData::Profile>' do
83
83
  res = @role.users
84
84
  expect(res).to be_an_instance_of(Array)
85
85
  res.each do |user|
86
- expect(user).to be_an_instance_of(GoodData::AccountSettings)
86
+ expect(user).to be_an_instance_of(GoodData::Profile)
87
87
  end
88
88
  end
89
89
  end
@@ -11,13 +11,38 @@ describe GoodData::Project do
11
11
  GoodData.disconnect
12
12
  end
13
13
 
14
+ def load_users_from_csv
15
+ GoodData::Helpers::Csv.read(:path => CsvHelper::CSV_PATH_IMPORT, :header => true) do |row|
16
+ json = {
17
+ 'user' => {
18
+ 'content' => {
19
+ 'email' => row[2],
20
+ 'login' => row[2],
21
+ 'firstname' => row[0],
22
+ 'lastname' => row[1],
23
+
24
+ # Following lines are ugly hack
25
+ 'role' => row[6],
26
+ 'password' => row[3],
27
+ 'domain' => row[9],
28
+
29
+ # And following lines are even much more ugly hack
30
+ # 'sso_provider' => '',
31
+ # 'authentication_modes' => ['sso', 'password']
32
+ },
33
+ 'meta' => {}
34
+ }
35
+ }
36
+
37
+ GoodData::Membership.new(json)
38
+ end
39
+ end
40
+
14
41
  describe '#[]' do
15
42
  it 'Accepts :all parameter' do
16
- pending 'Investigate which credentials use'
17
-
18
- project = GoodData::Project[:all]
19
- project.should_not be_nil
20
- project.should be_a_kind_of(Array)
43
+ projects = GoodData::Project[:all]
44
+ projects.should_not be_nil
45
+ projects.should be_a_kind_of(Array)
21
46
  end
22
47
 
23
48
  it 'Returns project if ID passed' do
@@ -40,9 +65,9 @@ describe GoodData::Project do
40
65
 
41
66
  describe '#all' do
42
67
  it 'Returns all projects' do
43
- pending 'Investigate which credentials use'
44
-
45
- GoodData::Project.all
68
+ projects = GoodData::Project.all
69
+ projects.should_not be_nil
70
+ projects.should be_a_kind_of(Array)
46
71
  end
47
72
  end
48
73
 
@@ -73,14 +98,81 @@ describe GoodData::Project do
73
98
  end
74
99
  end
75
100
 
101
+ describe "#member" do
102
+ it 'Returns GoodData::Membership when looking for existing user using email' do
103
+ project = ProjectHelper.get_default_project
104
+ res = project.member('svarovsky+gem_tester@gooddata.com')
105
+ expect(res).to be_instance_of(GoodData::Membership)
106
+ end
107
+
108
+ it 'Returns GoodData::Membership when looking for existing user using URL' do
109
+ project = ProjectHelper.get_default_project
110
+ res = project.member(ConnectionHelper::DEFAULT_USER_URL)
111
+ expect(res).to be_instance_of(GoodData::Membership)
112
+ end
113
+
114
+ it 'Returns GoodData::Membership when looking for existing user using GoodData::Profile' do
115
+ project = ProjectHelper.get_default_project
116
+ user = project.members.first
117
+ res = project.member(user)
118
+ expect(res).to be_instance_of(GoodData::Membership)
119
+ end
120
+
121
+ it 'Returns null for non-existing user' do
122
+ project = ProjectHelper.get_default_project
123
+ res = project.member('jan.kokotko@gooddata.com')
124
+ res.should be_nil
125
+ end
126
+ end
127
+
128
+ describe "#member?" do
129
+ it 'Returns true when looking for existing user using email' do
130
+ project = ProjectHelper.get_default_project
131
+ res = project.member?('svarovsky+gem_tester@gooddata.com')
132
+ res.should be_true
133
+ end
134
+
135
+ it 'Returns true when looking for existing user using URL' do
136
+ project = ProjectHelper.get_default_project
137
+ res = project.member?(ConnectionHelper::DEFAULT_USER_URL)
138
+ res.should be_true
139
+ end
140
+
141
+ it 'Returns true when looking for existing user using GoodData::Profile' do
142
+ project = ProjectHelper.get_default_project
143
+ user = project.members.first
144
+ res = project.member?(user)
145
+ res.should be_true
146
+ end
147
+
148
+ it 'Returns false for non-existing user' do
149
+ project = ProjectHelper.get_default_project
150
+ res = project.member?('jan.kokotko@gooddata.com')
151
+ res.should be_false
152
+ end
153
+
154
+ it 'Returns true for existing user when using optional list' do
155
+ project = ProjectHelper.get_default_project
156
+ list = project.members
157
+ res = project.member?('svarovsky+gem_tester@gooddata.com', list)
158
+ res.should be_true
159
+ end
160
+
161
+ it 'Returns false for non-existing user when using optional list' do
162
+ project = ProjectHelper.get_default_project
163
+ list = []
164
+ res = project.member?('svarovsky+gem_tester@gooddata.com', list)
165
+ res.should be_false
166
+ end
167
+ end
168
+
76
169
  describe '#processes' do
77
170
  it 'Returns the processes' do
78
- pending 'Investigate which credentials to use'
79
171
 
80
172
  GoodData.project = ProjectHelper::PROJECT_ID
81
-
82
173
  proj = GoodData.project
83
- procs = proj.processes
174
+ processes = proj.processes
175
+ expect(processes).to be_a_kind_of(Array)
84
176
  end
85
177
  end
86
178
 
@@ -98,7 +190,6 @@ describe GoodData::Project do
98
190
 
99
191
  describe '#users' do
100
192
  it 'Returns array of GoodData::Users' do
101
- pending 'Investigate which credentials to use'
102
193
 
103
194
  project = GoodData::Project[ProjectHelper::PROJECT_ID]
104
195
 
@@ -110,7 +201,7 @@ describe GoodData::Project do
110
201
  expect(users).to be_instance_of(Array)
111
202
 
112
203
  users.each do |user|
113
- expect(user).to be_instance_of(GoodData::User)
204
+ expect(user).to be_instance_of(GoodData::Membership)
114
205
 
115
206
  roles = user.roles
116
207
  roles.should_not be_nil
@@ -140,4 +231,126 @@ describe GoodData::Project do
140
231
  end
141
232
  end
142
233
  end
234
+
235
+ describe '#users_create' do
236
+ it 'Creates new users' do
237
+ project = ProjectHelper.get_default_project
238
+
239
+ users = (0...10).map do |i|
240
+ num = rand(1e6)
241
+ login = "gemtest#{num}@gooddata.com"
242
+
243
+ json = {
244
+ 'user' => {
245
+ 'content' => {
246
+ 'email' => login,
247
+ 'login' => login,
248
+ 'firstname' => 'the',
249
+ 'lastname' => num.to_s,
250
+
251
+ # Following lines are ugly hack
252
+ 'role' => 'admin',
253
+ 'password' => 'password',
254
+ 'domain' => ConnectionHelper::DEFAULT_DOMAIN,
255
+
256
+ # And following lines are even much more ugly hack
257
+ # 'sso_provider' => '',
258
+ # 'authentication_modes' => ['sso', 'password']
259
+ },
260
+ 'meta' => {}
261
+ }
262
+ }
263
+
264
+ GoodData::Membership.new(json)
265
+ end
266
+
267
+ res = GoodData::Domain.users_create(users)
268
+
269
+ project.users_create(users)
270
+
271
+ expect(res).to be_an_instance_of(Array)
272
+ res.each do |r|
273
+ expect(r).to be_an_instance_of(GoodData::Profile)
274
+ r.delete
275
+ end
276
+ end
277
+ end
278
+
279
+ describe '#users_import' do
280
+ it 'Import users from CSV' do
281
+
282
+ project = GoodData::Project[ProjectHelper::PROJECT_ID]
283
+
284
+ new_users = load_users_from_csv
285
+
286
+ project.users_import(new_users)
287
+ end
288
+ end
289
+
290
+ describe '#set_user_roles' do
291
+ it 'Properly updates user roles as needed' do
292
+ project = ProjectHelper.get_default_project
293
+
294
+ project.set_user_roles(ConnectionHelper::DEFAULT_USERNAME, 'admin')
295
+ end
296
+ end
297
+
298
+ describe '#set_users_roles' do
299
+ it 'Properly updates user roles as needed for bunch of users' do
300
+ project = ProjectHelper.get_default_project
301
+
302
+ list = load_users_from_csv
303
+
304
+ # Create domain users
305
+ domain_users = GoodData::Domain.users_create(list, ConnectionHelper::DEFAULT_DOMAIN)
306
+ expect(domain_users.length).to equal(list.length)
307
+
308
+ # Create list with user, desired_roles hashes
309
+ domain_users.each_with_index do |user, index|
310
+ list[index] = {
311
+ :user => user,
312
+ :roles => list[index].json['user']['content']['role'].split(' ').map { |r| r.downcase }.sort
313
+ }
314
+ end
315
+
316
+ res = project.set_users_roles(list)
317
+ expect(res.length).to equal(list.length)
318
+ res.each do |update_result|
319
+ expect(update_result[:result]['projectUsersUpdateResult']['successful'][0]).to include(update_result[:user].uri)
320
+ end
321
+
322
+ domain_users.each do |user|
323
+ user.delete if user.email != ConnectionHelper::DEFAULT_USERNAME
324
+ end
325
+ end
326
+
327
+ it 'Properly updates user roles when user specified by email and :roles specified as array of string with role names' do
328
+ project = ProjectHelper.get_default_project
329
+
330
+ list = [
331
+ {
332
+ :user => ConnectionHelper::DEFAULT_USERNAME,
333
+ :roles => ['admin']
334
+ }
335
+ ]
336
+
337
+ res = project.set_users_roles(list)
338
+ expect(res.length).to equal(list.length)
339
+ end
340
+
341
+ it 'Properly updates user roles when user specified by email and :roles specified as string with role name' do
342
+ project = ProjectHelper.get_default_project
343
+
344
+ list = [
345
+ {
346
+ :user => ConnectionHelper::DEFAULT_USERNAME,
347
+ :roles => 'admin'
348
+ }
349
+ ]
350
+
351
+ res = project.set_users_roles(list)
352
+ expect(res.length).to equal(list.length)
353
+ end
354
+
355
+ end
143
356
  end
@@ -9,6 +9,13 @@ describe GoodData::Schedule, :broken => true do
9
9
  :cron => '2 2 2 2 *'
10
10
  }
11
11
 
12
+ TEST_DATA_WITH_OPTIONAL_PARAM = {
13
+ :timezone => 'UTC',
14
+ :cron => '2 2 2 2 *',
15
+ :reschedule => 15
16
+ }
17
+
18
+
12
19
  TEST_PROCESS_ID = 'f12975d2-5958-4248-9c3d-4c8f2e1f067d'
13
20
 
14
21
  SCHEDULE_ID = '53642303e4b0ae3190e464a8'
@@ -72,6 +79,16 @@ describe GoodData::Schedule, :broken => true do
72
79
  sched.delete
73
80
  end
74
81
 
82
+ it 'Creates new schedule if mandatory params passed and optional params are present' do
83
+ sched = nil
84
+ expect {
85
+ sched = GoodData::Schedule.create(TEST_PROCESS_ID, TEST_CRON, @project_executable, TEST_DATA_WITH_OPTIONAL_PARAM)
86
+ }.not_to raise_error
87
+
88
+ sched.should_not be_nil
89
+ sched.delete
90
+ end
91
+
75
92
  it 'Throws exception when no process ID specified' do
76
93
  expect {
77
94
  sched = GoodData::Schedule.create(nil, TEST_CRON, @project_executable, TEST_DATA)
@@ -489,4 +506,45 @@ describe GoodData::Schedule, :broken => true do
489
506
  expect(@schedule.dirty).to eq(true)
490
507
  end
491
508
  end
509
+
510
+ describe '#reschedule' do
511
+ before(:each) do
512
+ @schedule = GoodData::Schedule.create(TEST_PROCESS_ID, TEST_CRON, @project_executable, TEST_DATA_WITH_OPTIONAL_PARAM)
513
+ end
514
+
515
+ after(:each) do
516
+ @schedule.delete
517
+ end
518
+
519
+ it 'Should return reschedule as integer' do
520
+ res = @schedule.reschedule
521
+ res.should_not be_nil
522
+ res.should_not be_empty
523
+ res.should be_a_kind_of(Integer)
524
+ end
525
+
526
+
527
+
528
+ end
529
+
530
+ describe '#reschedule=' do
531
+ before(:each) do
532
+ @schedule = GoodData::Schedule.create(TEST_PROCESS_ID, TEST_CRON, @project_executable, TEST_DATA_WITH_OPTIONAL_PARAM)
533
+ end
534
+
535
+ after(:each) do
536
+ @schedule.delete
537
+ end
538
+
539
+ it 'Assigns the reschedule and marks the object dirty' do
540
+ test_reschedule = 45
541
+
542
+ @schedule.reschedule = test_reschedule
543
+ expect(@schedule.reschedule).to eq(test_reschedule)
544
+ expect(@schedule.dirty).to eq(true)
545
+ end
546
+ end
547
+
548
+
549
+
492
550
  end