gooddata 0.6.16 → 0.6.17

Sign up to get free protection for your applications and to get access to all the features.
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