gooddata 0.6.24 → 0.6.25

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 (114) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +54 -0
  3. data/CHANGELOG.md +3 -0
  4. data/DEPENDENCIES.md +155 -154
  5. data/README.md +15 -6
  6. data/Rakefile +5 -3
  7. data/dependency_decisions.yml +2 -0
  8. data/gooddata.gemspec +2 -3
  9. data/lib/gooddata/cli/cli.rb +1 -3
  10. data/lib/gooddata/cli/commands/auth_cmd.rb +16 -7
  11. data/lib/gooddata/cli/commands/project_cmd.rb +16 -178
  12. data/lib/gooddata/cli/shared.rb +46 -44
  13. data/lib/gooddata/commands/auth.rb +4 -0
  14. data/lib/gooddata/commands/project.rb +7 -24
  15. data/lib/gooddata/exceptions/object_migration.rb +4 -0
  16. data/lib/gooddata/exceptions/segment_not_empty.rb +18 -0
  17. data/lib/gooddata/extensions/object.rb +12 -0
  18. data/lib/gooddata/goodzilla/goodzilla.rb +56 -9
  19. data/lib/gooddata/helpers/global_helpers.rb +92 -0
  20. data/lib/gooddata/mixins/md_finders.rb +2 -8
  21. data/lib/gooddata/mixins/md_grantees.rb +42 -0
  22. data/lib/gooddata/mixins/md_id_to_uri.rb +1 -8
  23. data/lib/gooddata/mixins/md_object_id.rb +1 -1
  24. data/lib/gooddata/mixins/md_object_indexer.rb +5 -8
  25. data/lib/gooddata/mixins/md_object_query.rb +2 -2
  26. data/lib/gooddata/mixins/not_group.rb +17 -0
  27. data/lib/gooddata/mixins/rest_getters.rb +2 -2
  28. data/lib/gooddata/mixins/rest_resource.rb +1 -0
  29. data/lib/gooddata/mixins/to_json.rb +11 -0
  30. data/lib/gooddata/mixins/uri_getter.rb +9 -0
  31. data/lib/gooddata/models/blueprint/anchor_field.rb +14 -0
  32. data/lib/gooddata/models/blueprint/project_blueprint.rb +15 -1
  33. data/lib/gooddata/models/blueprint/to_wire.rb +10 -0
  34. data/lib/gooddata/models/client.rb +178 -0
  35. data/lib/gooddata/models/client_synchronization_result.rb +31 -0
  36. data/lib/gooddata/models/client_synchronization_result_details.rb +41 -0
  37. data/lib/gooddata/models/datawarehouse.rb +1 -5
  38. data/lib/gooddata/models/domain.rb +85 -1
  39. data/lib/gooddata/models/execution.rb +0 -2
  40. data/lib/gooddata/models/execution_detail.rb +0 -2
  41. data/lib/gooddata/models/from_wire.rb +10 -0
  42. data/lib/gooddata/models/invitation.rb +1 -1
  43. data/lib/gooddata/models/links.rb +1 -1
  44. data/lib/gooddata/models/membership.rb +10 -6
  45. data/lib/gooddata/models/metadata.rb +98 -11
  46. data/lib/gooddata/models/metadata/attribute.rb +6 -7
  47. data/lib/gooddata/models/metadata/dashboard.rb +41 -75
  48. data/lib/gooddata/models/metadata/dashboard/dashboard_item.rb +20 -4
  49. data/lib/gooddata/models/metadata/dashboard/filter_apply_item.rb +37 -0
  50. data/lib/gooddata/models/metadata/dashboard/filter_item.rb +49 -0
  51. data/lib/gooddata/models/metadata/dashboard/geo_chart_item.rb +56 -0
  52. data/lib/gooddata/models/metadata/dashboard/headline_item.rb +56 -0
  53. data/lib/gooddata/models/metadata/dashboard/iframe_item.rb +46 -0
  54. data/lib/gooddata/models/metadata/dashboard/report_item.rb +49 -8
  55. data/lib/gooddata/models/metadata/dashboard/text_item.rb +55 -0
  56. data/lib/gooddata/models/metadata/dashboard_tab.rb +83 -30
  57. data/lib/gooddata/models/metadata/dataset.rb +0 -2
  58. data/lib/gooddata/models/metadata/dimension.rb +1 -3
  59. data/lib/gooddata/models/metadata/fact.rb +1 -3
  60. data/lib/gooddata/models/metadata/label.rb +1 -3
  61. data/lib/gooddata/models/metadata/metric.rb +11 -42
  62. data/lib/gooddata/models/metadata/report.rb +7 -18
  63. data/lib/gooddata/models/metadata/report_definition.rb +21 -113
  64. data/lib/gooddata/models/metadata/scheduled_mail.rb +274 -0
  65. data/lib/gooddata/models/metadata/scheduled_mail/dashboard_attachment.rb +62 -0
  66. data/lib/gooddata/models/metadata/scheduled_mail/report_attachment.rb +64 -0
  67. data/lib/gooddata/models/metadata/variable.rb +8 -2
  68. data/lib/gooddata/models/model.rb +2 -9
  69. data/lib/gooddata/models/process.rb +7 -29
  70. data/lib/gooddata/models/profile.rb +1 -1
  71. data/lib/gooddata/models/project.rb +131 -167
  72. data/lib/gooddata/models/project_creator.rb +2 -7
  73. data/lib/gooddata/models/project_metadata.rb +2 -10
  74. data/lib/gooddata/models/project_role.rb +4 -10
  75. data/lib/gooddata/models/report_data_result.rb +3 -5
  76. data/lib/gooddata/models/schedule.rb +4 -31
  77. data/lib/gooddata/models/segment.rb +192 -0
  78. data/lib/gooddata/models/user_filters/mandatory_user_filter.rb +2 -2
  79. data/lib/gooddata/models/user_filters/user_filter_builder.rb +1 -1
  80. data/lib/gooddata/models/user_filters/variable_user_filter.rb +11 -0
  81. data/lib/gooddata/models/user_group.rb +241 -0
  82. data/lib/gooddata/rest/connection.rb +81 -16
  83. data/lib/gooddata/rest/object.rb +29 -0
  84. data/lib/gooddata/rest/object_factory.rb +6 -1
  85. data/lib/gooddata/rest/resource.rb +7 -1
  86. data/lib/gooddata/version.rb +1 -1
  87. data/spec/environment/default.rb +19 -16
  88. data/spec/environment/develop.rb +10 -10
  89. data/spec/environment/hotfix.rb +6 -6
  90. data/spec/environment/production.rb +14 -14
  91. data/spec/environment/release.rb +6 -6
  92. data/spec/environment/staging.rb +9 -9
  93. data/spec/environment/staging_3.rb +14 -15
  94. data/spec/integration/blueprint_with_grain_spec.rb +72 -0
  95. data/spec/integration/clients_spec.rb +135 -0
  96. data/spec/integration/date_dim_switch_spec.rb +142 -0
  97. data/spec/integration/full_project_spec.rb +3 -3
  98. data/spec/integration/project_spec.rb +20 -0
  99. data/spec/integration/segments_spec.rb +141 -0
  100. data/spec/integration/user_group_spec.rb +127 -0
  101. data/spec/spec_helper.rb +4 -0
  102. data/spec/unit/models/domain_spec.rb +7 -1
  103. data/spec/unit/models/metric_spec.rb +0 -8
  104. data/spec/unit/models/profile_spec.rb +1 -1
  105. data/spec/unit/models/report_result_data_spec.rb +6 -0
  106. metadata +38 -38
  107. data/lib/gooddata/cli/commands/api_cmd.rb +0 -34
  108. data/lib/gooddata/cli/commands/console_cmd.rb +0 -40
  109. data/lib/gooddata/cli/commands/domain_cmd.rb +0 -46
  110. data/lib/gooddata/cli/commands/process_cmd.rb +0 -145
  111. data/lib/gooddata/cli/commands/projects_cmd.rb +0 -23
  112. data/lib/gooddata/cli/commands/run_ruby_cmd.rb +0 -77
  113. data/lib/gooddata/cli/commands/scaffold_cmd.rb +0 -35
  114. data/lib/gooddata/cli/commands/user_cmd.rb +0 -24
@@ -0,0 +1,72 @@
1
+ # encoding: UTF-8
2
+ #
3
+ # Copyright (c) 2010-2015 GoodData Corporation. All rights reserved.
4
+ # This source code is licensed under the BSD-style license found in the
5
+ # LICENSE file in the root directory of this source tree.
6
+
7
+ require 'gooddata'
8
+
9
+ describe 'Create project using GoodData client', :constraint => 'slow' do
10
+ before(:all) do
11
+ @client = ConnectionHelper.create_default_connection
12
+ @blueprint = GoodData::Model::ProjectBlueprint.build("My project from blueprint") do |p|
13
+ p.add_date_dimension('created_on')
14
+
15
+ p.add_dataset('dataset.users') do |d|
16
+ d.add_anchor('attr.users.id', grain: [{ date: 'created_on' }, { attribute: 'attribute.user' }])
17
+ d.add_date('created_on')
18
+ d.add_attribute('attribute.user')
19
+ d.add_label('label.user.email', reference: 'attribute.user')
20
+ d.add_fact('fact.users.some_number')
21
+ end
22
+ end
23
+ @blueprint.valid? # => true
24
+
25
+ @project = @client.create_project_from_blueprint(@blueprint, auth_token: ConnectionHelper::GD_PROJECT_TOKEN)
26
+ end
27
+
28
+ after(:all) do
29
+ @project.delete
30
+ @client.disconnect
31
+ end
32
+
33
+ it 'Should load the data with grain' do
34
+ data = [
35
+ ['created_on', 'label.user.email', 'fact.users.some_number'],
36
+ ['01/01/2011', 'thomas', '1'],
37
+ ['01/01/2011', 'thomas', '2'],
38
+ ['01/01/2011', 'jim', '2'],
39
+ ['01/01/2011', 'peter', '2'],
40
+ ['01/01/2011', 'john', '3']]
41
+ @project.upload(data, @blueprint, 'dataset.users')
42
+
43
+ expect(@project.blueprint.datasets.first.count(@project)).to eq 4
44
+ end
45
+
46
+ it 'Should be able to remove grain and load the same data' do
47
+ updated_blueprint = GoodData::Model::ProjectBlueprint.build("My project from blueprint") do |p|
48
+ p.add_date_dimension('created_on')
49
+
50
+ p.add_dataset('dataset.users') do |d|
51
+ d.add_anchor('attr.users.id')
52
+ d.add_date('created_on')
53
+ d.add_attribute('attribute.user')
54
+ d.add_label('label.user.email', reference: 'attribute.user')
55
+ d.add_fact('fact.users.some_number')
56
+ end
57
+ end
58
+
59
+ @project.update_from_blueprint(updated_blueprint)
60
+
61
+ data = [
62
+ ['created_on', 'label.user.email', 'fact.users.some_number'],
63
+ ['01/01/2011', 'thomas', '1'],
64
+ ['01/01/2011', 'thomas', '2'],
65
+ ['01/01/2011', 'jim', '2'],
66
+ ['01/01/2011', 'peter', '2'],
67
+ ['01/01/2011', 'john', '3']]
68
+ @project.upload(data, @blueprint, 'dataset.users')
69
+
70
+ expect(@project.blueprint.datasets.first.count(@project)).to eq 5
71
+ end
72
+ end
@@ -0,0 +1,135 @@
1
+ # encoding: UTF-8
2
+ #
3
+ # Copyright (c) 2010-2015 GoodData Corporation. All rights reserved.
4
+ # This source code is licensed under the BSD-style license found in the
5
+ # LICENSE file in the root directory of this source tree.
6
+
7
+ require 'gooddata/models/segment'
8
+ require 'securerandom'
9
+
10
+ describe GoodData::Client do
11
+ TOKEN = 'mustangs'
12
+
13
+ before(:all) do
14
+ @client = GoodData.connect('mustang@gooddata.com', 'jindrisska', server: 'https://mustangs.intgdc.com', verify_ssl: false )
15
+ @domain = @client.domain('mustangs')
16
+ @master_project = @client.create_project(title: 'Test project', auth_token: TOKEN)
17
+ @segment_name = "segment-#{SecureRandom.uuid}"
18
+ @segment = @domain.create_segment(segment_id: @segment_name, master_project: @master_project)
19
+ end
20
+
21
+ after(:all) do
22
+ @segment && @segment.delete
23
+ @master_project && @master_project.delete
24
+ @client.disconnect
25
+ end
26
+
27
+ describe '#[]' do
28
+ before(:all) do
29
+ client_id = SecureRandom.uuid
30
+ @client_project = @client.create_project(title: "client_#{client_id} project", auth_token: TOKEN)
31
+ @segment_client = @segment.create_client(id: "tenant_#{client_id}", project: @client_project)
32
+ end
33
+
34
+ it 'Returns all clients of a segment' do
35
+ clients = @segment.clients
36
+ expect(clients.to_a).to be_an_instance_of(Array)
37
+ expect(clients.to_a.count).to eq 1
38
+ end
39
+
40
+ it 'Returns specific tenant when schedule ID passed' do
41
+ client = @segment.clients(@segment_client.uri)
42
+ expect(client).to be_an_instance_of(GoodData::Client)
43
+ expect(client.uri).to eq @segment_client.uri
44
+ end
45
+
46
+ after(:all) do
47
+ @segment_client && @segment_client.delete
48
+ @client_project && @client_project.delete
49
+ end
50
+
51
+ end
52
+
53
+ describe '#delete' do
54
+ before(:all) do
55
+ client_id = SecureRandom.uuid
56
+ @segment_client = @segment.create_client(id: "tenant_#{client_id}")
57
+ end
58
+
59
+ it 'Deletes particular client' do
60
+ expect(@segment.clients.count).to eq 1
61
+ s = @segment.clients(@segment_client.uri)
62
+ s.delete
63
+ expect(@segment.clients.count).to eq 0
64
+ @segment_client = nil
65
+ end
66
+
67
+ after(:all) do
68
+ @segment_client && @segment_client.delete
69
+ end
70
+ end
71
+
72
+ describe '#delete' do
73
+ before(:all) do
74
+ client_id = SecureRandom.uuid
75
+ @client_project = @client.create_project(title: 'client_1 project', auth_token: TOKEN)
76
+ @segment_client = @segment.create_client(id: "tenant_#{client_id}", project: @client_project)
77
+ end
78
+
79
+ it 'Deletes particular client. Project is cleaned up as well' do
80
+ expect(@segment.clients.count).to eq 1
81
+ s = @segment.clients(@segment_client.uri)
82
+ s.delete
83
+ expect(@segment.clients.count).to eq 0
84
+ expect(@client_project.reload!.state).to eq :deleted
85
+ @segment_client = nil
86
+ end
87
+
88
+ after(:all) do
89
+ @segment_client && segment_client.delete
90
+ end
91
+ end
92
+
93
+ describe '#save' do
94
+ before(:all) do
95
+ @client_id = SecureRandom.uuid
96
+ @client_project = @client.create_project(title: 'client_1 project', auth_token: TOKEN)
97
+ @segment_client = @segment.create_client(id: "tenant_#{@client_id}", project: @client_project)
98
+ end
99
+
100
+ it 'can update project id' do
101
+ begin
102
+ other_client_project = @client.create_project(title: "client_#{@client_id} other project", auth_token: TOKEN)
103
+ @segment_client.project = other_client_project
104
+ @segment_client.save
105
+ expect(@segment.clients('tenant_1').project_uri).to eq other_client_project.uri
106
+ ensure
107
+ other_client_project && other_client_project.delete
108
+ end
109
+ end
110
+
111
+ it 'can update tenants segment id' do
112
+ pending 'Fix the test '
113
+
114
+ second_segment_name = "segment-#{SecureRandom.uuid}"
115
+ second_master_project = @client.create_project(title: 'Test project', auth_token: TOKEN)
116
+ second_segment = @domain.create_segment(segment_id: second_segment_name, master_project: second_master_project)
117
+ @segment_client.segment = second_segment
118
+ @segment_client.save
119
+ expect(second_segment.clients.find { |s| s.uri == @segment_client.uri }).not_to be_nil
120
+ expect(@segment.clients.find { |s| s.uri == @segment_client.uri }).to be_nil
121
+ end
122
+
123
+ it 'cannot update a client id' do
124
+ @segment_client.client_id = 'different_id'
125
+ expect {
126
+ @segment_client.save
127
+ }.to raise_error RestClient::BadRequest
128
+ end
129
+
130
+ after(:all) do
131
+ @segment_client && @segment_client.delete
132
+ @client_project && @client_project.delete
133
+ end
134
+ end
135
+ end
@@ -0,0 +1,142 @@
1
+ # encoding: UTF-8
2
+ #
3
+ # Copyright (c) 2010-2015 GoodData Corporation. All rights reserved.
4
+ # This source code is licensed under the BSD-style license found in the
5
+ # LICENSE file in the root directory of this source tree.
6
+
7
+ require 'gooddata'
8
+
9
+ describe "Swapping a date dimension and exchanging all attributes/elements", :constraint => 'slow' do
10
+ before(:all) do
11
+ @client = ConnectionHelper::create_default_connection
12
+ @blueprint = GoodData::Model::ProjectBlueprint.build("My project from blueprint") do |p|
13
+ p.add_date_dimension('created_on')
14
+ p.add_date_dimension('created_on_2')
15
+
16
+ p.add_dataset('dataset.users') do |d|
17
+ d.add_anchor('attr.users.id')
18
+ d.add_label('label.users.id', reference: 'attr.users.id')
19
+ d.add_date('created_on')
20
+ d.add_fact('fact.users.some_number')
21
+ end
22
+ end
23
+
24
+ # Create a project
25
+ @project = @client.create_project_from_blueprint(@blueprint, token: ConnectionHelper::GD_PROJECT_TOKEN, environment: ProjectHelper::ENVIRONMENT)
26
+
27
+ # Load data
28
+ users_data = [
29
+ ["label.users.id", "created_on", "fact.users.some_number"],
30
+ [1,"01/01/2014",1],
31
+ [2,"10/15/2014",2],
32
+ [3,"05/02/2014",3]
33
+ ]
34
+ @project.upload(users_data, @blueprint, 'dataset.users')
35
+
36
+ end
37
+
38
+ after(:all) do
39
+ @project.delete unless @project.nil?
40
+
41
+ @client.disconnect
42
+ end
43
+
44
+ it "should swap the dimension, exhcange all stuff and not break anything" do
45
+
46
+ # WE have 2 date dims
47
+ expect(@blueprint.date_dimensions.map(&:id)).to eq ["created_on", "created_on_2"]
48
+ # One is connected
49
+ expect(@blueprint.datasets.flat_map(&:references).map(&:reference).include?('created_on')).to be_truthy
50
+ # The other is disconnected
51
+ expect(@blueprint.datasets.flat_map(&:references).map(&:reference).include?('created_on_2')).to be_falsey
52
+
53
+
54
+ # Create a metric
55
+ @metric_1 = @project.attributes('created_on.date').create_metric(title: 'Count of days in DD')
56
+ @metric_1.save
57
+
58
+ # Create a report
59
+ @report = @project.create_report(left: @metric_1, top: ['created_on.date'], title: 'Beautiful report')
60
+ @report.save
61
+
62
+ def suggest_mapping(label_a, label_b, project)
63
+ as = project.attributes
64
+ a1 = as.select {|a| a.identifier.include?("#{label_a}.")}.pmapcat { |a| [a] + a.labels }
65
+ a2 = as.select {|a| a.identifier.include?("#{label_b}.")}.pmapcat { |a| [a] + a.labels }
66
+ a1.reduce({}) do |a, e|
67
+ match = a2.find { |l| l.identifier.gsub(/^#{label_b}/, '') == e.identifier.gsub(/^#{label_a}/, '') }
68
+ a[e.identifier] = match && match.identifier
69
+ a
70
+ end
71
+ end
72
+
73
+ # Create definition with report specific metric
74
+ @metric_2 = @project.attributes('created_on.date').create_metric(title: 'Count of days in DD Secret')
75
+ @metric_2.deprecated = true
76
+ @metric_2.save
77
+ @report_with_private = @project.create_report({
78
+ left: @metric_2,
79
+ top: ['created_on.day.in.week'],
80
+ title: 'Beautiful report with private'
81
+ })
82
+ @report_with_private.save
83
+
84
+ # Create a definition with a filter value
85
+ @report = @project.create_report({
86
+ left: @metric_2,
87
+ top: ['created_on.quarter'],
88
+ filters: [['created_on.year', 2015, 2016]],
89
+ title: 'Beautiful report with filters'
90
+ })
91
+ @report.save
92
+
93
+ @label = @project.labels('created_on.year').primary_label
94
+ @variable = @project.create_variable(title: 'uaaa', attribute: @label.attribute).save
95
+ filters = [[ConnectionHelper::DEFAULT_USERNAME, @label.uri, '2015', '2016']]
96
+ @project.add_variable_permissions(filters, @variable)
97
+
98
+ as = @project.attributes.select {|a| a.identifier.include?('created_on_2.')}
99
+ expect(as.pmapcat {|a| a.usedby('report')}).to be_empty
100
+ expect(as.pmapcat {|a| a.usedby('metric')}).to be_empty
101
+ expect(as.pmapcat {|a| a.usedby('dashboard')}).to be_empty
102
+
103
+ # replace values
104
+ mapping = GoodData::Helpers.prepare_mapping(suggest_mapping('created_on', 'created_on_2', @project), project: @project)
105
+ @project.replace_from_mapping(mapping)
106
+
107
+ # Check if any of the attributes is used by any of the objects. All should be empty
108
+ as = @project.attributes.select {|a| a.identifier.include?('created_on.')}
109
+ expect(as.pmapcat {|a| a.usedby('report')}).to be_empty
110
+ expect(as.pmapcat {|a| a.usedby('metric')}).to be_empty
111
+ expect(as.pmapcat {|a| a.usedby('dashboard')}).to be_empty
112
+
113
+ # Reload stuff
114
+ @metric_2.reload!
115
+ @report.reload!
116
+ @report_with_private.reload!
117
+
118
+ # Stuff should be still computable
119
+ @metric_2.execute
120
+ @report.execute
121
+ @report_with_private.execute
122
+
123
+ # Labels
124
+ GoodData::SmallGoodZilla.get_uris(@metric_2.expression)
125
+ expect(@report.definition.attributes.map(&:identifier)).to eq ["created_on_2.quarter"]
126
+ ids = GoodData::SmallGoodZilla.get_uris(@report.definition.filters.first).map { |x| x.split('/')[-2..-1].join('/') }
127
+ expect(ids).to eq ["obj/2286", "2286/elements?id=2015", "2286/elements?id=2016"]
128
+
129
+ ids = GoodData::SmallGoodZilla.get_uris(@report.definition.filters.first).map { |x| x.split('/')[-2..-1].join('/') }
130
+ expect(ids).to eq ["obj/2286", "2286/elements?id=2015", "2286/elements?id=2016"]
131
+
132
+ ids = GoodData::SmallGoodZilla.get_uris(@variable.values.first.expression).map {|v| v.split('/')[-2..-1].join('/')}
133
+ expect(ids).to eq ["obj/2286", "2286/elements?id=2015", "2286/elements?id=2016"]
134
+
135
+ # Swap the dims
136
+ bp = @project.blueprint
137
+ bp.swap_date_dimension!('created_on', 'created_on_2')
138
+ @project.update_from_blueprint(bp)
139
+ expect(bp.datasets.flat_map(&:references).map(&:reference).include?('created_on')).to be_falsey
140
+ expect(bp.datasets.flat_map(&:references).map(&:reference).include?('created_on_2')).to be_truthy
141
+ end
142
+ end
@@ -226,6 +226,7 @@ describe "Full project implementation", :constraint => 'slow' do
226
226
  expect(r.locked?).to eq true
227
227
  r.unlock!
228
228
  expect(r.locked?).to eq false
229
+ r.delete
229
230
  end
230
231
 
231
232
  it "should be able to purge report from older revisions" do
@@ -365,7 +366,7 @@ describe "Full project implementation", :constraint => 'slow' do
365
366
  fact = @project.fact_by_title('Lines Changed')
366
367
  expect(fact.tags.empty?).to be_truthy
367
368
 
368
- fact.tags = "tag1,tag2,tag3"
369
+ fact.tags = "tag1 tag2 tag3"
369
370
  fact.save
370
371
 
371
372
  tagged_facts = GoodData::Fact.find_by_tag('tag3', :client => @client, :project => @project)
@@ -413,8 +414,7 @@ describe "Full project implementation", :constraint => 'slow' do
413
414
 
414
415
  expect(metric.contain?(attribute)).to be true
415
416
  expect(metric.contain?(repo_attribute)).to be false
416
-
417
- metric.replace(attribute, repo_attribute)
417
+ metric.replace!(attribute => repo_attribute)
418
418
  metric.save
419
419
  expect(metric.execute).not_to eq 4
420
420
 
@@ -207,6 +207,26 @@ describe GoodData::Project, :constraint => 'slow' do
207
207
  expect(logins.map {|l| users.find {|u| u.login == l}}.pmap {|u| u.role.title}).to eq roles.flatten
208
208
 
209
209
  end
210
+
211
+ it 'can work with groups as well. Groups have to be set up. It can only eat hashes on input in this case' do
212
+ users = (1..5).to_a.map { |x| ProjectHelper.create_random_user(@client).to_hash }
213
+ group_names = ['group_1', 'group_2']
214
+ groups = group_names.map { |g| @project.create_group(name: g) }
215
+ users_with_groups = users.map { |u| u[:user_group] = groups.take(rand(2) + 1).map(&:name); u}
216
+ @domain.create_users(users_with_groups, domain: @domain)
217
+ @project.import_users(users_with_groups, domain: @domain, whitelists: [/gem_tester@gooddata.com/])
218
+ expect(users_with_groups.flat_map { |u| u[:user_group].map { |g| [u[:login], g] } }.all? do |u, g|
219
+ @project.user_groups(g).member?(@project.member(u).uri) rescue false
220
+ end).to be_truthy
221
+
222
+ users_with_group = users.map { |u| u[:user_group] = ['group_1']; u}
223
+ to_whitelist = @project.user_groups('group_2').members.to_a.sample
224
+ @project.import_users(users_with_group, domain: @domain, whitelists: [to_whitelist.login, /gem_tester@gooddata.com/])
225
+ expect(@project.user_groups('group_2').members.map(&:login)).to eq [to_whitelist.login]
226
+ expect(users_with_group.flat_map { |u| u[:user_group].map { |g| [u[:login], g] } }.all? do |u, g|
227
+ @project.user_groups(g).member?(@project.member(u).uri) rescue false
228
+ end).to be_truthy
229
+ end
210
230
  end
211
231
 
212
232
  describe '#summary' do
@@ -0,0 +1,141 @@
1
+ # encoding: UTF-8
2
+ #
3
+ # Copyright (c) 2010-2015 GoodData Corporation. All rights reserved.
4
+ # This source code is licensed under the BSD-style license found in the
5
+ # LICENSE file in the root directory of this source tree.
6
+
7
+ require 'gooddata/models/segment'
8
+ require 'securerandom'
9
+
10
+ describe GoodData::Segment do
11
+ TOKEN = 'mustangs'
12
+
13
+ before(:all) do
14
+ @client = GoodData.connect('mustang@gooddata.com', 'jindrisska', server: 'https://mustangs.intgdc.com', verify_ssl: false )
15
+ @domain = @client.domain('mustangs')
16
+ end
17
+
18
+ before(:each) do
19
+ @uuid = SecureRandom.uuid
20
+ @master_project = @client.create_project(title: "Test MASTER project for #{@uuid}", auth_token: TOKEN)
21
+ @segment_name = "segment-#{@uuid}"
22
+ @segment = @domain.create_segment(segment_id: @segment_name, master_project: @master_project)
23
+ end
24
+
25
+ after(:each) do
26
+ @segment && @segment.delete(force: true)
27
+ end
28
+
29
+ after(:all) do
30
+ @master_project.delete if @master_project
31
+ @client.disconnect
32
+ end
33
+
34
+ describe '#[]' do
35
+ it 'Returns all segments when :all passed' do
36
+ res = @domain.segments
37
+ expect(res).to be_an_instance_of(Array)
38
+ end
39
+
40
+ it 'Returns specific segment when segment ID passed' do
41
+ s = @domain.segments(@segment_name)
42
+ @segment.uri == s.uri
43
+ expect(s).to be_an_instance_of(GoodData::Segment)
44
+ expect(@segment).to be_an_instance_of(GoodData::Segment)
45
+ end
46
+ end
47
+
48
+ describe '#delete' do
49
+ it 'Deletes particular segment' do
50
+ old_count = @domain.segments.count
51
+ s = @domain.segments(@segment_name)
52
+ s.delete
53
+ expect(@domain.segments.length).to eq (old_count - 1)
54
+ # prevent delete attempt in the after hook
55
+ @segment = nil
56
+ end
57
+ end
58
+
59
+ describe '#save' do
60
+ it 'can update a segment master project' do
61
+ different_master = @client.create_project(title: 'Test project', auth_token: TOKEN)
62
+ @segment.master_project = different_master
63
+ @segment.save
64
+ @segment = @domain.segments(@segment_name)
65
+ expect(@segment.master_project_uri).not_to eq @master_project.uri
66
+ expect(@segment.master_project_uri).to eq different_master.uri
67
+ end
68
+
69
+ it 'cannot update a segment id' do
70
+ @segment.segment_id = 'different_id'
71
+ expect {
72
+ @segment.save
73
+ }.to raise_error RestClient::BadRequest
74
+ end
75
+ end
76
+
77
+ describe '#create_client' do
78
+ it 'can create a new client in a segment' do
79
+ begin
80
+ client_project = @client.create_project(title: 'client_1 project', auth_token: TOKEN)
81
+ segment_client = @segment.create_client(id: 'tenant_1', project: client_project)
82
+ expect(segment_client).to be_an_instance_of(GoodData::Client)
83
+ expect(@segment.clients.count).to eq 1
84
+ ensure
85
+ segment_client && segment_client.delete
86
+ end
87
+ end
88
+ end
89
+
90
+ describe '#provision_client_projects' do
91
+ it 'can create a new client in a segment without project and then provision' do
92
+ begin
93
+ segment_client = @segment.create_client(id: 'tenant_1')
94
+ expect(segment_client).to be_an_instance_of(GoodData::Client)
95
+ expect(@segment.clients.count).to eq 1
96
+ @domain.synchronize_clients
97
+ @domain.provision_client_projects
98
+ expect(@domain.segments.flat_map { |s| s.clients.to_a }.all?(&:project?)).to be_truthy
99
+ ensure
100
+ segment_client && segment_client.delete
101
+ end
102
+ end
103
+ end
104
+
105
+ describe '#update_clients' do
106
+ it 'can create a new client in a segment without project and then provision' do
107
+ begin
108
+ uuid_2 = SecureRandom.uuid
109
+ master_project_2 = @client.create_project(title: "Test MASTER project for #{uuid_2}", auth_token: TOKEN)
110
+ segment_name_2 = "segment-#{uuid_2}"
111
+ segment_2 = @domain.create_segment(segment_id: segment_name_2, master_project: master_project_2)
112
+
113
+ client_1 = "client-#{SecureRandom.uuid}"
114
+ client_2 = "client-#{SecureRandom.uuid}"
115
+ data = [{id: client_1, segment: segment_name_2 },
116
+ {id: client_2, segment: @segment_name }]
117
+ res = @domain.update_clients(data)
118
+ expect(@domain.segments.map(&:id)).to include(@segment.id, segment_2.id)
119
+ expect(@domain.segments.pmapcat {|s| s.clients.to_a }.map(&:id)).to include(client_1, client_2)
120
+
121
+ client_3 = "client-#{SecureRandom.uuid}"
122
+ client_4 = "client-#{SecureRandom.uuid}"
123
+ data = [{id: client_3, segment: segment_name_2 },
124
+ {id: client_4, segment: @segment_name }]
125
+ res = @domain.update_clients(data)
126
+ expect(@domain.segments.pmapcat {|s| s.clients.to_a }.map(&:id)).to include(client_1, client_2, client_3, client_4)
127
+
128
+ # bring the projects
129
+ @domain.synchronize_clients
130
+ @domain.provision_client_projects
131
+ projects_to_delete = @domain.segments.pmapcat {|s| s.clients.to_a }.select { |c| [client_1, client_2].include?(c.id) }.map(&:project)
132
+ res = @domain.update_clients(data, delete_extra: true)
133
+ expect(@domain.segments.pmapcat {|s| s.clients.to_a }.map(&:id)).to include(client_3, client_4)
134
+ expect(@domain.segments.pmapcat {|s| s.clients.to_a }.map(&:id)).not_to include(client_1, client_2)
135
+ expect(projects_to_delete.pmap(&:reload!).map(&:state)).to eq [:deleted, :deleted]
136
+ ensure
137
+ segment_2.delete(force: true) if segment_2
138
+ end
139
+ end
140
+ end
141
+ end