gooddata 0.6.16 → 0.6.17

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 (43) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +4 -1
  3. data/lib/gooddata/cli/commands/project_cmd.rb +1 -1
  4. data/lib/gooddata/core/logging.rb +15 -5
  5. data/lib/gooddata/core/rest.rb +4 -28
  6. data/lib/gooddata/helpers/global_helpers.rb +14 -138
  7. data/lib/gooddata/helpers/global_helpers_params.rb +145 -0
  8. data/lib/gooddata/mixins/md_object_indexer.rb +2 -2
  9. data/lib/gooddata/models/domain.rb +1 -1
  10. data/lib/gooddata/models/execution.rb +29 -1
  11. data/lib/gooddata/models/from_wire.rb +6 -0
  12. data/lib/gooddata/models/from_wire_parse.rb +125 -0
  13. data/lib/gooddata/models/metadata/attribute.rb +1 -1
  14. data/lib/gooddata/models/metadata/label.rb +11 -10
  15. data/lib/gooddata/models/model.rb +4 -0
  16. data/lib/gooddata/models/profile.rb +12 -2
  17. data/lib/gooddata/models/project.rb +6 -3
  18. data/lib/gooddata/models/project_blueprint.rb +4 -4
  19. data/lib/gooddata/models/project_creator.rb +8 -10
  20. data/lib/gooddata/models/report_data_result.rb +4 -2
  21. data/lib/gooddata/models/schedule.rb +121 -66
  22. data/lib/gooddata/models/to_wire.rb +12 -3
  23. data/lib/gooddata/models/user_filters/user_filter_builder.rb +3 -234
  24. data/lib/gooddata/models/user_filters/user_filter_builder_create.rb +115 -0
  25. data/lib/gooddata/models/user_filters/user_filter_builder_execute.rb +133 -0
  26. data/lib/gooddata/rest/client.rb +27 -13
  27. data/lib/gooddata/rest/connection.rb +102 -23
  28. data/lib/gooddata/version.rb +1 -1
  29. data/spec/data/gd_gse_data_blueprint.json +1 -0
  30. data/spec/data/test_project_model_spec.json +5 -2
  31. data/spec/data/wire_models/model_view.json +3 -0
  32. data/spec/data/wire_test_project.json +8 -1
  33. data/spec/integration/full_project_spec.rb +1 -1
  34. data/spec/unit/core/connection_spec.rb +16 -0
  35. data/spec/unit/core/logging_spec.rb +54 -6
  36. data/spec/unit/models/domain_spec.rb +10 -4
  37. data/spec/unit/models/execution_spec.rb +102 -0
  38. data/spec/unit/models/from_wire_spec.rb +11 -2
  39. data/spec/unit/models/model_spec.rb +2 -2
  40. data/spec/unit/models/project_blueprint_spec.rb +1 -1
  41. data/spec/unit/models/schedule_spec.rb +34 -24
  42. data/spec/unit/models/to_wire_spec.rb +9 -1
  43. metadata +8 -3
@@ -40,7 +40,7 @@ describe "Full project implementation", :constraint => 'slow' do
40
40
  expect(results).to be_nil
41
41
 
42
42
  # When we change the model using the original blueprint. Basically change the title back.
43
- results = GoodData::Model::ProjectCreator.migrate_datasets(@spec, project: @project, client: @client)
43
+ results = @project.update_from_blueprint(@spec)
44
44
  # It should offer no changes using the original blueprint
45
45
  results = GoodData::Model::ProjectCreator.migrate_datasets(@spec, project: @project, client: @client, dry_run: true)
46
46
  expect(results).to be_nil
@@ -32,4 +32,20 @@ describe GoodData::Rest::Connection do
32
32
  c.disconnect
33
33
  end
34
34
  end
35
+
36
+ describe '#generate_request_id' do
37
+ it "Generates a non-empty string" do
38
+ c = ConnectionHelper.create_default_connection
39
+
40
+ # generate a request id, and pass it to a request
41
+ id = c.generate_request_id
42
+ resp = c.get('/gdc/md', :request_id => id)
43
+
44
+ id.should be_a(String)
45
+ id.should_not be_empty
46
+
47
+ c.disconnect
48
+ end
49
+ end
50
+
35
51
  end
@@ -1,6 +1,30 @@
1
1
  # encoding: UTF-8
2
2
 
3
3
  require 'gooddata/core/logging'
4
+ require 'logger'
5
+
6
+ # Logger that remembers the last logged message
7
+ class TestLogger < Logger
8
+ attr_reader :last_message
9
+ def debug(*args)
10
+ @last_message = args[0]
11
+ super(*args)
12
+ end
13
+ def info(*args)
14
+ @last_message = args[0]
15
+ super(*args)
16
+ end
17
+ def warn(*args)
18
+ @last_message = args[0]
19
+ super(*args)
20
+ end
21
+ def error(*args)
22
+ @last_message = args[0]
23
+ super(*args)
24
+ end
25
+ end
26
+
27
+
4
28
 
5
29
  describe 'GoodData - logging' do
6
30
  TEST_MESSAGE = 'Hello World!'
@@ -17,21 +41,45 @@ describe 'GoodData - logging' do
17
41
  GoodData.logger.warn TEST_MESSAGE
18
42
  end
19
43
 
44
+ def test_request_id_logging
45
+ c = ConnectionHelper.create_default_connection
46
+ id = c.generate_request_id
47
+ GoodData.logger.info "Request id: #{id} Doing something very useful"
48
+ c.get('/gdc/md', :request_id => id)
49
+ id
50
+ end
51
+
20
52
  def test_all
21
53
  test_error
22
54
  test_info
23
- test_info
55
+ test_warn
56
+ test_request_id_logging
24
57
  end
25
58
 
26
59
  before(:each) do
27
- @is_logging_on = GoodData.logging_on?
28
-
29
- # TODO: Use some kind of 'reset' instead
30
- GoodData.logging_on if !@is_logging_on
60
+ # remember the state of logging before
61
+ @logging_on_at_start = GoodData.logging_on?
31
62
  end
32
63
 
33
64
  after(:each) do
34
- GoodData.logging_off if !@is_logging_on
65
+ # restore the logging state
66
+ if @logging_on_at_start
67
+ GoodData.logging_on
68
+ else
69
+ GoodData.logging_off
70
+ end
71
+ end
72
+
73
+ describe '#logger' do
74
+ it "can assign a custom logger" do
75
+ GoodData.logger = TestLogger.new(STDOUT)
76
+ test_all
77
+ end
78
+ it 'has the request id logged when I passed it' do
79
+ GoodData.logger = TestLogger.new(STDOUT)
80
+ id = test_request_id_logging
81
+ expect(GoodData.logger.last_message).to include(id)
82
+ end
35
83
  end
36
84
 
37
85
 
@@ -125,12 +125,18 @@ describe GoodData::Domain do
125
125
  .reject { |u| u.login == ConnectionHelper::DEFAULT_USERNAME }.sample
126
126
 
127
127
  old_email = user.email
128
+ old_sso_provider = user.sso_provider || ''
128
129
  user.email = 'john.doe@gooddata.com'
130
+ user.sso_provider = user.sso_provider ? user.sso_provider.reverse : 'some_sso_provider'
129
131
  @domain.update_user(user)
130
- expect(@domain.get_user(user.login).email).to eq 'john.doe@gooddata.com'
131
- user.email = old_email
132
- @domain.update_user(user)
133
- expect(@domain.get_user(user.login).email).to eq old_email
132
+ updated_user = @domain.find_user_by_login(user.login)
133
+ expect(updated_user.email).to eq 'john.doe@gooddata.com'
134
+ expect(updated_user.sso_provider).to eq 'some_sso_provider'
135
+ updated_user.email = old_email
136
+ updated_user.sso_provider = old_sso_provider
137
+ @domain.update_user(updated_user)
138
+ expect(@domain.find_user_by_login(user.login).email).to eq old_email
139
+ expect(@domain.find_user_by_login(user.login).sso_provider).to eq old_sso_provider
134
140
  end
135
141
  end
136
142
  end
@@ -0,0 +1,102 @@
1
+ require 'gooddata/models/schedule'
2
+
3
+ describe GoodData::Execution do
4
+
5
+ before(:each) do
6
+ @data = {"execution"=>
7
+ {"startTime"=>"2015-02-27T15:44:21.759Z",
8
+ "endTime"=>"2015-02-27T15:47:49.383Z",
9
+ "log"=>
10
+ "/gdc/projects/tk3b994vmdpcb0xjwexc9moen8t5bpiw/dataload/processes/2b031451-b1a2-4039-8e36-0672542a0e60/executions/54f090d5e4b0c9cbdcb0f45b/log",
11
+ "status"=>"OK",
12
+ "trigger"=>"MANUAL",
13
+ "links"=>
14
+ {"self"=>
15
+ "/gdc/projects/tk3b994vmdpcb0xjwexc9moen8t5bpiw/schedules/54f08d1de4b0c9cbdcb0f323/executions/54f090d5e4b0c9cbdcb0f45b"},
16
+ "createdTime"=>"2015-02-27T15:44:21.361Z"}}
17
+ @execution = GoodData::Execution.new(@data)
18
+ end
19
+
20
+ describe '#created' do
21
+ it 'returns created as a Time instance' do
22
+ expect(@execution.created.class).to eq Time
23
+ expect(@execution.created.to_s).to eq '2015-02-27 15:44:21 UTC'
24
+ end
25
+ end
26
+
27
+ describe '#error?' do
28
+ it 'returns true if executione errored out' do
29
+ expect(@execution.error?).to be_falsy
30
+ end
31
+ end
32
+
33
+ describe '#ok?' do
34
+ it 'returns true if executione finished ok' do
35
+ expect(@execution.ok?).to be_truthy
36
+ end
37
+ end
38
+
39
+ describe '#finished' do
40
+ it 'returns time when execution finished' do
41
+ expect(@execution.finished.class).to eq Time
42
+ expect(@execution.finished.to_s).to eq '2015-02-27 15:47:49 UTC'
43
+ end
44
+
45
+ it 'returns nil if it is not finished' do
46
+ @data['execution']['status'] = 'RUNNING'
47
+ @data['execution']['endTime'] = nil
48
+ running_execution = GoodData::Execution.new(@data)
49
+ expect(running_execution.finished).to be_nil
50
+ end
51
+ end
52
+
53
+ describe '#schedule_uri' do
54
+ it 'returns uri of schedule that was executed' do
55
+ expect(@execution.schedule_uri).to eq '/gdc/projects/tk3b994vmdpcb0xjwexc9moen8t5bpiw/schedules/54f08d1de4b0c9cbdcb0f323'
56
+ end
57
+ end
58
+
59
+ describe '#running?' do
60
+ it 'returns false if executione is already finished' do
61
+ expect(@execution.running?).to be_falsy
62
+ end
63
+
64
+ it 'returns true if executione is currently finished' do
65
+ @data['execution']['status'] = 'RUNNING'
66
+ running_execution = GoodData::Execution.new(@data)
67
+ expect(running_execution.running?).to be_truthy
68
+ end
69
+ end
70
+
71
+ describe '#started' do
72
+ it 'returns time when execution started' do
73
+ expect(@execution.started.class).to eq Time
74
+ expect(@execution.started.to_s).to eq '2015-02-27 15:44:21 UTC'
75
+ end
76
+ end
77
+
78
+ describe '#status' do
79
+ it 'returns :ok for finished execution' do
80
+ expect(@execution.status).to eq :ok
81
+ end
82
+ end
83
+
84
+ describe '#uri' do
85
+ it 'returns time when execution started' do
86
+ expect(@execution.uri).to eq '/gdc/projects/tk3b994vmdpcb0xjwexc9moen8t5bpiw/schedules/54f08d1de4b0c9cbdcb0f323/executions/54f090d5e4b0c9cbdcb0f45b'
87
+ end
88
+ end
89
+
90
+ describe '#duration' do
91
+ it 'returns time it took to run execution' do
92
+ expect(@execution.duration).to eq 207.624
93
+ end
94
+
95
+ it 'returns nil if it is not finished' do
96
+ @data['execution']['status'] = 'RUNNING'
97
+ @data['execution']['endTime'] = nil
98
+ running_execution = GoodData::Execution.new(@data)
99
+ expect(running_execution.duration.class).to eq Float
100
+ end
101
+ end
102
+ end
@@ -42,11 +42,11 @@ describe GoodData::Model::FromWire do
42
42
  end
43
43
 
44
44
  it "should enable sorting" do
45
- pending("UAAA")
45
+ skip("UAAA")
46
46
  end
47
47
 
48
48
  it "should allow defining date dimensions" do
49
- pending('UAAA')
49
+ skip('UAAA')
50
50
  end
51
51
 
52
52
  it "should generate the same thing it parsed" do
@@ -61,6 +61,7 @@ describe GoodData::Model::FromWire do
61
61
  {
62
62
  type: 'anchor',
63
63
  name: "techoppanalysis",
64
+ folder: "Opportunity Benchmark",
64
65
  title: "Tech Opp. Analysis",
65
66
  gd_data_type: "VARCHAR(128)",
66
67
  gd_type: "GDC.text",
@@ -73,6 +74,7 @@ describe GoodData::Model::FromWire do
73
74
  expect(x).to eq [
74
75
  {
75
76
  :type=>'attribute',
77
+ :folder => "Opportunity Benchmark",
76
78
  :name=>"month",
77
79
  :gd_data_type=>"VARCHAR(128)",
78
80
  :gd_type=>"GDC.text",
@@ -88,6 +90,7 @@ describe GoodData::Model::FromWire do
88
90
  },
89
91
  {
90
92
  :type=>'attribute',
93
+ :folder => "Opportunity Benchmark",
91
94
  :name=>"cohorttype",
92
95
  :title=>"Cohort Type",
93
96
  :gd_data_type=>"VARCHAR(128)",
@@ -117,6 +120,12 @@ describe GoodData::Model::FromWire do
117
120
  }]
118
121
  end
119
122
 
123
+ it "should be able to parse description from both attributes and facts" do
124
+ expect(@blueprint.find_dataset('opportunity').anchor[:description]).to eq 'This is opportunity attribute description'
125
+ expect(@blueprint.find_dataset('stage_history').facts.find {|f| f[:name] == 'stage_velocity'}[:description]).to eq 'Velocity description'
126
+ expect(@blueprint.find_dataset('opp_owner').attributes.find {|f| f[:name] == 'region'}[:description]).to eq 'Owner Region description'
127
+ end
128
+
120
129
  it "should be able to deal with fiscal dimensions with weird names" do
121
130
  model_view = MultiJson.load(File.read('./spec/data/wire_models/nu_model.json'))
122
131
  blueprint = FromWire.from_wire(model_view)
@@ -16,7 +16,7 @@ describe GoodData::Model do
16
16
  {
17
17
  :name => "commits",
18
18
  :columns => [
19
- {:type => "fact", :name => "lines_changed"}
19
+ {:type => "fact", :name => "lines_changed", :description=>"Fact description"}
20
20
  ]
21
21
  }
22
22
  ]})
@@ -49,7 +49,7 @@ describe GoodData::Model do
49
49
  stuff = GoodData::Model.merge_dataset_columns(first_dataset, additional_blueprint)
50
50
 
51
51
  stuff[:columns].count.should == 4
52
- stuff[:columns].include?({:type => "fact", :name => "lines_changed"}).should == true
52
+ stuff[:columns].include?({:type => "fact", :name => "lines_changed", :description=>"Fact description"}).should == true
53
53
  stuff[:columns].group_by { |col| col[:name] }["lines_changed"].count.should == 1
54
54
  end
55
55
 
@@ -174,7 +174,7 @@ describe GoodData::Model::ProjectBlueprint do
174
174
  end
175
175
  dataset = builder.to_blueprint
176
176
  @blueprint.datasets.count.should == 3
177
- @blueprint.add_dataset(dataset)
177
+ @blueprint.add_dataset!(dataset)
178
178
  @blueprint.datasets.count.should == 4
179
179
  end
180
180
 
@@ -4,8 +4,6 @@ describe GoodData::Schedule do
4
4
  SCHEDULE_ID = ScheduleHelper::SCHEDULE_ID
5
5
  SCHEDULE_URL = "/gdc/projects/#{ProjectHelper::PROJECT_ID}/schedules/#{SCHEDULE_ID}"
6
6
 
7
- @test_cron = '0 15 27 7 *'
8
-
9
7
  before(:all) do
10
8
  @client = ConnectionHelper.create_default_connection
11
9
 
@@ -20,11 +18,16 @@ describe GoodData::Schedule do
20
18
 
21
19
  @project = ProjectHelper.get_default_project(:client => @client)
22
20
  @project_executable = 'graph/graph.grf'
21
+ @test_cron = '0 15 27 7 *'
23
22
  @test_data = {
24
23
  :timezone => 'UTC',
25
24
  :cron => '2 2 2 2 *',
26
25
  :client => @client,
27
- :project => @project
26
+ :project => @project,
27
+ :params => {
28
+ 'a' => 'b',
29
+ 'b' => 'c'
30
+ }
28
31
  }
29
32
 
30
33
  @test_data_with_optional_param = {
@@ -76,12 +79,8 @@ describe GoodData::Schedule do
76
79
 
77
80
  describe '#create' do
78
81
  it 'Creates new schedule if mandatory params passed' do
79
- schedule = nil
80
82
  begin
81
- expect {
82
- schedule = @project.create_schedule(ProcessHelper::PROCESS_ID, @test_cron, @project_executable, @test_data)
83
- }.not_to raise_error
84
-
83
+ schedule = @project.create_schedule(ProcessHelper::PROCESS_ID, @test_cron, @project_executable, @test_data)
85
84
  expect(schedule).to be_truthy
86
85
  ensure
87
86
  schedule && schedule.delete
@@ -89,12 +88,8 @@ describe GoodData::Schedule do
89
88
  end
90
89
 
91
90
  it 'Creates new schedule if mandatory params passed and optional params are present' do
92
- schedule = nil
93
91
  begin
94
- expect {
95
- schedule = @project.create_schedule(ProcessHelper::PROCESS_ID, @test_cron, @project_executable, @test_data_with_optional_param)
96
- }.not_to raise_error
97
-
92
+ schedule = @project.create_schedule(ProcessHelper::PROCESS_ID, @test_cron, @project_executable, @test_data_with_optional_param)
98
93
  expect(schedule).to be_truthy
99
94
  ensure
100
95
  schedule && schedule.delete
@@ -138,25 +133,26 @@ describe GoodData::Schedule do
138
133
 
139
134
  it 'Throws exception when no timezone specified' do
140
135
  data = @test_data.deep_dup
141
- data[:timezone] = nil
142
- schedule = nil
136
+ schedule = @project.create_schedule(ProcessHelper::PROCESS_ID, @test_cron, @project_executable, data)
137
+ schedule.timezone = nil
143
138
  begin
144
139
  expect {
145
- schedule = @project.create_schedule(ProcessHelper::PROCESS_ID, @test_cron, @project_executable, data)
140
+ schedule.save
146
141
  }.to raise_error 'A timezone has to be provided'
147
142
  ensure
148
143
  schedule && schedule.delete
149
144
  end
150
145
  end
151
146
 
152
- it 'Throws exception when no timezone specified' do
153
- data = @test_data.deep_dup
154
- data[:type] = nil
147
+ it 'Throws exception when no schedule type is specified' do
155
148
  schedule = nil
149
+ data = @test_data.deep_dup
156
150
  begin
157
- expect {
158
151
  schedule = @project.create_schedule(ProcessHelper::PROCESS_ID, @test_cron, @project_executable, data)
159
- }.to raise_error 'Schedule type has to be provided'
152
+ schedule.type = nil
153
+ expect {
154
+ schedule.save
155
+ }.to raise_error 'Schedule type has to be provided'
160
156
  ensure
161
157
  schedule && schedule.delete
162
158
  end
@@ -165,13 +161,13 @@ describe GoodData::Schedule do
165
161
 
166
162
  describe '#cron' do
167
163
  it 'Should return cron as string' do
168
- schedule = nil
169
164
  begin
170
165
  schedule = @project.create_schedule(ProcessHelper::PROCESS_ID, @test_cron, @project_executable, @test_data)
171
166
  res = schedule.cron
172
167
  res.should_not be_nil
173
168
  res.should_not be_empty
174
169
  res.should be_a_kind_of(String)
170
+ expect(schedule.time_based?).to be_truthy
175
171
  ensure
176
172
  schedule && schedule.delete
177
173
  end
@@ -334,12 +330,14 @@ describe GoodData::Schedule do
334
330
  old_params = schedule.params
335
331
 
336
332
  test_params = {
337
- 'PROCESS_ID' => '1-2-3-4'
333
+ 'some_new_param' => '1-2-3-4'
338
334
  }
339
335
 
340
336
  schedule.params = test_params
341
- expect(schedule.params).to eq(old_params.merge(test_params))
337
+ expect(schedule.params.keys).to eq(%w(PROCESS_ID EXECUTABLE some_new_param))
338
+ expect(schedule.params['some_new_param']).to eq '1-2-3-4'
342
339
  expect(schedule.dirty).to eq(true)
340
+ schedule.save
343
341
  ensure
344
342
  schedule && schedule.delete
345
343
  end
@@ -486,4 +484,16 @@ describe GoodData::Schedule do
486
484
  end
487
485
  end
488
486
  end
487
+
488
+ describe '#executions' do
489
+ it 'Returns executions' do
490
+ begin
491
+ schedule = @project.create_schedule(ProcessHelper::PROCESS_ID, @test_cron, @project_executable, @test_data_with_optional_param)
492
+ expect(schedule.executions).to be_empty
493
+ schedule.execute
494
+ ensure
495
+ schedule && schedule.delete
496
+ end
497
+ end
498
+ end
489
499
  end