gooddata 0.6.6 → 0.6.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. checksums.yaml +4 -4
  2. data/{CHANGELOG.markdown → CHANGELOG.md} +6 -1
  3. data/lib/gooddata/cli/commands/project_cmd.rb +6 -29
  4. data/lib/gooddata/commands/project.rb +36 -44
  5. data/lib/gooddata/connection.rb +7 -0
  6. data/lib/gooddata/core/connection.rb +3 -0
  7. data/lib/gooddata/core/rest.rb +38 -19
  8. data/lib/gooddata/mixins/md_object_query.rb +3 -2
  9. data/lib/gooddata/models/metadata/dashboard.rb +3 -8
  10. data/lib/gooddata/models/metadata/report.rb +1 -6
  11. data/lib/gooddata/models/model.rb +5 -4
  12. data/lib/gooddata/models/process.rb +10 -14
  13. data/lib/gooddata/models/project.rb +34 -26
  14. data/lib/gooddata/models/project_blueprint.rb +14 -7
  15. data/lib/gooddata/models/project_creator.rb +2 -6
  16. data/lib/gooddata/models/schedule.rb +13 -2
  17. data/lib/gooddata/version.rb +1 -1
  18. data/spec/data/ruby_process/deep_files/deep_stuff.txt +1 -0
  19. data/spec/data/ruby_process/process.rb +2 -0
  20. data/spec/data/ruby_process/stuff.txt +1 -0
  21. data/spec/integration/full_process_schedule_spec.rb +69 -0
  22. data/spec/integration/full_project_spec.rb +14 -5
  23. data/spec/unit/models/schedule_spec.rb +7 -30
  24. metadata +7 -66
  25. data/doc/.gitignore +0 -1
  26. data/doc/css/.gitkeepme +0 -1
  27. data/doc/images/.gitkeepme +0 -1
  28. data/doc/images/background.png +0 -0
  29. data/doc/images/bg-callout-button.png +0 -0
  30. data/doc/images/header-logo.png +0 -0
  31. data/doc/images/logo-image.png +0 -0
  32. data/doc/js/.gitkeepme +0 -1
  33. data/doc/pages/GET_STARTED.md +0 -310
  34. data/doc/pages/HOMEPAGE.md +0 -77
  35. data/doc/pages/TUTORIALS.md +0 -52
  36. data/doc/pages/tutorial/BRICKS.md +0 -260
  37. data/doc/pages/tutorial/CREATING_A_MODEL.md +0 -81
  38. data/doc/pages/tutorial/CRUNCHING_NUMBERS.md +0 -231
  39. data/doc/pages/tutorial/TEST_DRIVEN_DEVELOPMENT.md +0 -120
  40. data/doc/pages/tutorial/YOUR_FIRST_PROJECT.md +0 -53
  41. data/doc/templates/default/class/dot/setup.rb +0 -6
  42. data/doc/templates/default/class/dot/superklass.erb +0 -3
  43. data/doc/templates/default/class/setup.rb +0 -37
  44. data/doc/templates/default/class/text/setup.rb +0 -11
  45. data/doc/templates/default/class/text/subclasses.erb +0 -5
  46. data/doc/templates/default/constant/text/header.erb +0 -11
  47. data/doc/templates/default/constant/text/setup.rb +0 -3
  48. data/doc/templates/default/docstring/setup.rb +0 -51
  49. data/doc/templates/default/docstring/text/abstract.erb +0 -2
  50. data/doc/templates/default/docstring/text/deprecated.erb +0 -2
  51. data/doc/templates/default/docstring/text/index.erb +0 -2
  52. data/doc/templates/default/docstring/text/note.erb +0 -4
  53. data/doc/templates/default/docstring/text/private.erb +0 -2
  54. data/doc/templates/default/docstring/text/returns_void.erb +0 -1
  55. data/doc/templates/default/docstring/text/text.erb +0 -1
  56. data/doc/templates/default/docstring/text/todo.erb +0 -4
  57. data/doc/templates/default/layout/dot/header.erb +0 -6
  58. data/doc/templates/default/layout/dot/setup.rb +0 -14
  59. data/doc/templates/default/method/setup.rb +0 -3
  60. data/doc/templates/default/method/text/header.erb +0 -1
  61. data/doc/templates/default/method_details/setup.rb +0 -11
  62. data/doc/templates/default/method_details/text/header.erb +0 -10
  63. data/doc/templates/default/method_details/text/method_signature.erb +0 -12
  64. data/doc/templates/default/method_details/text/setup.rb +0 -10
  65. data/doc/templates/default/module/dot/child.erb +0 -1
  66. data/doc/templates/default/module/dot/dependencies.erb +0 -3
  67. data/doc/templates/default/module/dot/header.erb +0 -6
  68. data/doc/templates/default/module/dot/info.erb +0 -14
  69. data/doc/templates/default/module/dot/setup.rb +0 -14
  70. data/doc/templates/default/module/setup.rb +0 -164
  71. data/doc/templates/default/module/text/children.erb +0 -10
  72. data/doc/templates/default/module/text/class_meths_list.erb +0 -8
  73. data/doc/templates/default/module/text/extends.erb +0 -8
  74. data/doc/templates/default/module/text/header.erb +0 -7
  75. data/doc/templates/default/module/text/includes.erb +0 -8
  76. data/doc/templates/default/module/text/instance_meths_list.erb +0 -8
  77. data/doc/templates/default/module/text/setup.rb +0 -12
  78. data/doc/templates/default/root/dot/child.erb +0 -3
  79. data/doc/templates/default/root/dot/setup.rb +0 -5
  80. data/doc/templates/default/tags/setup.rb +0 -55
  81. data/doc/templates/default/tags/text/example.erb +0 -12
  82. data/doc/templates/default/tags/text/index.erb +0 -1
  83. data/doc/templates/default/tags/text/option.erb +0 -20
  84. data/doc/templates/default/tags/text/overload.erb +0 -19
  85. data/doc/templates/default/tags/text/see.erb +0 -11
  86. data/doc/templates/default/tags/text/tag.erb +0 -13
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9735f379edc31d6cef7c4aea2ce2230910e30a6f
4
- data.tar.gz: bfdc00d8beec59011127c8845eed2fac393cf0d0
3
+ metadata.gz: 57bce714f8ec1d419534b3a6f3730c430b5f67dd
4
+ data.tar.gz: 53a29990c0fb352dc481ff56b8361ccc25c6ff11
5
5
  SHA512:
6
- metadata.gz: ff1ab5206777958b9690d453d9416e538452a97571192c380a5c80aecd716842c3225720bfee445f75d2eb51bec9f2777c81d8e11d1c8ca588cad0c8c506565d
7
- data.tar.gz: 05141dcd016696cfa0b8f2edfcdd24785814100056e2b4e798592185e328feff0c6e5b17756884d4f526263ef9f06486b48b927f46725c857e7f25fc3918dc67
6
+ metadata.gz: ef33ff8a02e8385d2ba0b2fe80091a15f9ac6c69f10d69faf3dd95967e35e0a1095c3d409811d15e917a80fa7001d98e94d81a6396d28494943275d9b0f9bcf9
7
+ data.tar.gz: 2d9e907507561efc2c0e6de51037dde476c2d5a09a719abc27871fc964c671c14696feb2ef321373cf14bed0504ad45723a230c7f2a7292c3e1e4dba1321e8dc
@@ -1,8 +1,13 @@
1
1
  # GoodData Ruby SDK Changelog
2
2
 
3
- ## 0.6.6
3
+ ## 0.6.7
4
4
  - Fixed the scaffolding templates to take advantage of new syntax (missing references in dataset refs)
5
5
  - Fixing inclusion of extensions when using CLI
6
+ - Fixed pollers and added/fixed tests for schedules and processes
7
+ - Added with_connection which automatically disconnects when you are done
8
+
9
+ ## 0.6.6
10
+ - Various fixes
6
11
 
7
12
  ## 0.6.5
8
13
  - Mixins
@@ -14,33 +14,9 @@ GoodData::CLI.module_eval do
14
14
  c.desc 'If you are in a gooddata project blueprint or if you provide a project id it will start an interactive session inside that project'
15
15
  c.command :jack_in do |jack|
16
16
  jack.action do |global_options, options, args|
17
- goodfile_path = GoodData::Helpers.find_goodfile(Pathname('.'))
18
-
19
- spin_session = proc do |goodfile, blueprint|
20
- project_id = global_options[:project_id] || goodfile[:project_id]
21
- fail "You have to provide 'project_id'. You can either provide it through -p flag or even better way is to fill it in in your Goodfile under key \"project_id\". If you just started a project you have to create it first. One way might be through \"gooddata project build\"" if project_id.nil? || project_id.empty?
22
-
23
- opts = options.merge(global_options)
24
- GoodData.connect(opts)
25
-
26
- begin
27
- require 'gooddata'
28
- GoodData::Command::Project.jack_in(project_id)
29
- rescue GoodData::ProjectNotFound
30
- puts "Project with id \"#{project_id}\" could not be found. Make sure that the id you provided is correct."
31
- end
32
- end
33
-
34
- if goodfile_path
35
- goodfile = MultiJson.load(File.read(goodfile_path), :symbolize_keys => true)
36
- model_key = goodfile[:model]
37
- blueprint = GoodData::Model::ProjectBlueprint.new(eval(File.read(model_key)).to_hash) if File.exist?(model_key) && !File.directory?(model_key)
38
- FileUtils.cd(goodfile_path.dirname) do
39
- spin_session.call(goodfile, blueprint)
40
- end
41
- else
42
- spin_session.call({}, nil)
43
- end
17
+ opts = options.merge(global_options)
18
+ GoodData.connect(opts)
19
+ GoodData::Command::Project.jack_in(opts)
44
20
  end
45
21
  end
46
22
 
@@ -95,11 +71,12 @@ GoodData::CLI.module_eval do
95
71
  opts = options.merge(global_options)
96
72
  id = global_options[:project_id]
97
73
  token = opts[:token]
98
-
74
+ opts[:auth_token] = token
99
75
  fail 'You have to provide a token for creating a project. Please use parameter --token' if token.nil? || token.empty?
100
76
 
101
77
  GoodData.connect(opts)
102
- GoodData::Command::Project.clone(id, opts)
78
+ new_project = GoodData::Command::Project.clone(id, opts)
79
+ puts "Project with title \"#{new_project.title}\" was cloned with id #{new_project.pid}"
103
80
  end
104
81
  end
105
82
 
@@ -32,44 +32,9 @@ module GoodData
32
32
 
33
33
  # Clone existing project
34
34
  def clone(project_id, options)
35
- with_data = options[:data] || true
36
- with_users = options[:users] || false
37
- export = {
38
- :exportProject => {
39
- :exportUsers => with_users ? 1 : 0,
40
- :exportData => with_data ? 1 : 0
41
- }
42
- }
43
-
44
- result = GoodData.post("/gdc/md/#{project_id}/maintenance/export", export)
45
- export_token = result['exportArtifact']['token']
46
- status_url = result['exportArtifact']['status']['uri']
47
-
48
- state = GoodData.get(status_url)['taskState']['status']
49
- while state == 'RUNNING'
50
- sleep 5
51
- result = GoodData.get(status_url)
52
- state = result['taskState']['status']
53
- end
54
-
55
- old_project = GoodData::Project[project_id]
56
- project_uri = create(options.merge(:title => "Clone of #{old_project.title}"))
57
- new_project = GoodData::Project[project_uri]
58
-
59
- import = {
60
- :importProject => {
61
- :token => export_token
62
- }
63
- }
64
- result = GoodData.post("/gdc/md/#{new_project.obj_id}/maintenance/import", import)
65
- status_url = result['uri']
66
- state = GoodData.get(status_url)['taskState']['status']
67
- while state == 'RUNNING'
68
- sleep 5
69
- result = GoodData.get(status_url)
70
- state = result['taskState']['status']
35
+ GoodData.with_project(project_id) do |project|
36
+ project.clone(options)
71
37
  end
72
- true
73
38
  end
74
39
 
75
40
  # Delete existing project
@@ -142,13 +107,40 @@ module GoodData
142
107
  end
143
108
  end
144
109
 
145
- def jack_in(project_id)
146
- GoodData.with_project(project_id) do |project|
147
- puts "Use 'exit' to quit the live session. Use 'q' to jump out of displaying a large output."
148
- binding.pry(:quiet => true,
149
- :prompt => [proc do |target_self, nest_level, pry|
150
- 'project_live_sesion: '
151
- end])
110
+ def jack_in(options)
111
+ goodfile_path = GoodData::Helpers.find_goodfile(Pathname('.'))
112
+
113
+ spin_session = proc do |goodfile, blueprint|
114
+ project_id = options[:project_id] || goodfile[:project_id]
115
+ message = 'You have to provide "project_id". You can either provide it through -p flag'\
116
+ 'or even better way is to fill it in in your Goodfile under key "project_id".'\
117
+ 'If you just started a project you have to create it first. One way might be'\
118
+ 'through "gooddata project build"'
119
+ fail message if project_id.nil? || project_id.empty?
120
+
121
+ begin
122
+ require 'gooddata'
123
+ GoodData.with_project(project_id) do |project|
124
+ puts "Use 'exit' to quit the live session. Use 'q' to jump out of displaying a large output."
125
+ binding.pry(:quiet => true,
126
+ :prompt => [proc do |target_self, nest_level, pry|
127
+ 'project_live_sesion: '
128
+ end])
129
+ end
130
+ rescue GoodData::ProjectNotFound
131
+ puts "Project with id \"#{project_id}\" could not be found. Make sure that the id you provided is correct."
132
+ end
133
+ end
134
+
135
+ if goodfile_path
136
+ goodfile = MultiJson.load(File.read(goodfile_path), :symbolize_keys => true)
137
+ model_key = goodfile[:model]
138
+ blueprint = GoodData::Model::ProjectBlueprint.new(eval(File.read(model_key)).to_hash) if File.exist?(model_key) && !File.directory?(model_key)
139
+ FileUtils.cd(goodfile_path.dirname) do
140
+ spin_session.call(goodfile, blueprint)
141
+ end
142
+ else
143
+ spin_session.call({}, nil)
152
144
  end
153
145
  end
154
146
  end
@@ -67,5 +67,12 @@ module GoodData
67
67
  connection.status = :logged_in
68
68
  connection
69
69
  end
70
+
71
+ def with_connection(options = nil, second_options = nil, third_options = {}, &bl)
72
+ connection = connect(options, second_options, third_options)
73
+ bl.call(connection)
74
+ ensure
75
+ disconnect
76
+ end
70
77
  end
71
78
  end
@@ -346,6 +346,9 @@ module GoodData
346
346
  if content_type == 'application/json' || content_type == 'application/json;charset=UTF-8'
347
347
  result = response.to_str == '""' ? {} : MultiJson.load(response.to_str)
348
348
  GoodData.logger.debug "Response: #{result.inspect}"
349
+ elsif ['text/plain;charset=UTF-8', 'text/plain; charset=UTF-8', 'text/plain'].include?(content_type)
350
+ result = response
351
+ GoodData.logger.debug 'Response: plain text'
349
352
  elsif content_type == 'application/zip'
350
353
  result = response
351
354
  GoodData.logger.debug 'Response: a zipped stream'
@@ -3,6 +3,8 @@
3
3
  require_relative 'connection'
4
4
 
5
5
  module GoodData
6
+ DEFAULT_SLEEP_INTERVAL = 10
7
+
6
8
  class << self
7
9
  # Performs a HTTP GET request.
8
10
  #
@@ -95,36 +97,53 @@ module GoodData
95
97
  connection.download(file, where, options.merge(:staging_url => url))
96
98
  end
97
99
 
98
- def poll(result, key = nil, options = {}, &bl)
99
- sleep_interval = options[:sleep_interval] || 10
100
- link = bl ? bl.call(result) : result[key]['links']['poll']
100
+ # Generalizaton of poller. Since we have quite a variation of how async proceses are handled
101
+ # this is a helper that should help you with resources where the information about "Are we done"
102
+ # is the http code of response. By default we repeat as long as the code == 202. You can
103
+ # change the code if necessary. It expects the URI as an input where it can poll. It returns the
104
+ # value of last poll. In majority of cases these are the data that you need.
105
+ #
106
+ # @param link [String] Link for polling
107
+ # @param options [Hash] Options
108
+ # @return [Hash] Result of polling
109
+ def poll_on_code(link, options = {})
110
+ code = options[:code] || 202
111
+ sleep_interval = options[:sleep_interval] || DEFAULT_SLEEP_INTERVAL
101
112
  response = GoodData.get(link, :process => false)
102
- while response.code != 204
113
+ while response.code == code
103
114
  sleep sleep_interval
104
115
  GoodData.connection.retryable(:tries => 3, :on => RestClient::InternalServerError) do
105
116
  sleep sleep_interval
106
117
  response = GoodData.get(link, :process => false)
107
118
  end
108
119
  end
109
- end
110
-
111
- def poll_on_root(result, root, &bl)
112
- polling_url = bl.call(result)
113
- polling_result = GoodData.get(polling_url)
114
- while polling_result[root]
115
- sleep(3)
116
- polling_result = GoodData.get(polling_url)
120
+ if options[:process] == false
121
+ response
122
+ else
123
+ GoodData.get(link)
117
124
  end
118
- polling_result
119
125
  end
120
126
 
121
- def wait_for_polling_result(polling_url)
122
- polling_result = GoodData.get(polling_url)
123
- while polling_result['wTaskStatus'] && polling_result['wTaskStatus']['status'] == 'RUNNING'
124
- sleep(3)
125
- polling_result = GoodData.get(polling_url)
127
+ # Generalizaton of poller. Since we have quite a variation of how async proceses are handled
128
+ # this is a helper that should help you with resources where the information about "Are we done"
129
+ # is inside the response. It expects the URI as an input where it can poll and a block that should
130
+ # return either true -> 'meaning we are done' or false -> meaning sleep and repeat. It returns the
131
+ # value of last poll. In majority of cases these are the data that you need
132
+ #
133
+ # @param link [String] Link for polling
134
+ # @param options [Hash] Options
135
+ # @return [Hash] Result of polling
136
+ def poll_on_response(link, options = {}, &bl)
137
+ sleep_interval = options[:sleep_interval] || DEFAULT_SLEEP_INTERVAL
138
+ response = GoodData.get(link)
139
+ while bl.call(response)
140
+ sleep sleep_interval
141
+ GoodData.connection.retryable(:tries => 3, :on => RestClient::InternalServerError) do
142
+ sleep sleep_interval
143
+ response = GoodData.get(link)
144
+ end
126
145
  end
127
- polling_result
146
+ response
128
147
  end
129
148
  end
130
149
  end
@@ -24,8 +24,9 @@ module GoodData
24
24
  # @option options [Boolean] :full if passed true the subclass can decide to pull in full objects. This is desirable from the usability POV but unfortunately has negative impact on performance so it is not the default
25
25
  # @return [Array<GoodData::MdObject> | Array<Hash>] Return the appropriate metadata objects or their representation
26
26
  def query(query_obj_type, klass, options = {})
27
- fail(NoProjectError, 'Connect to a project before searching for an object') unless GoodData.project
28
- query_result = GoodData.get(GoodData.project.md['query'] + "/#{query_obj_type}/")['query']['entries']
27
+ project = options[:project] || GoodData.project
28
+ fail(NoProjectError, 'Connect to a project before searching for an object') unless project
29
+ query_result = GoodData.get(project.md['query'] + "/#{query_obj_type}/")['query']['entries']
29
30
  options[:full] ? query_result.map { |item| klass[item['link']] } : query_result
30
31
  end
31
32
 
@@ -86,14 +86,9 @@ module GoodData
86
86
  fail "Wrong format provied \"#{format}\". Only supports formats #{supported_formats.join(', ')}" unless supported_formats.include?(format)
87
87
  tab = options[:tab] || ''
88
88
 
89
- req_uri = "/gdc/projects/#{GoodData.project.uri}/clientexport"
90
- x = GoodData.post(req_uri, { 'clientExport' => { 'url' => "https://secure.gooddata.com/dashboard.html#project=#{GoodData.project.uri}&dashboard=#{uri}&tab=#{tab}&export=1", 'name' => title } }, :process => false)
91
- while x.code == 202
92
- sleep(1)
93
- uri = MultiJson.load(x.body)['asyncTask']['link']['poll']
94
- x = GoodData.get(uri, :process => false)
95
- end
96
- x
89
+ req_uri = "/gdc/projects/#{GoodData.project.pid}/clientexport"
90
+ x = GoodData.post(req_uri, 'clientExport' => { 'url' => "https://secure.gooddata.com/dashboard.html#project=#{GoodData.project.uri}&dashboard=#{uri}&tab=#{tab}&export=1", 'name' => title })
91
+ GoodData.poll_on_code(x['asyncTask']['link']['poll'], process: false)
97
92
  end
98
93
 
99
94
  def tabs
@@ -103,12 +103,7 @@ module GoodData
103
103
  def export(format)
104
104
  result = GoodData.post('/gdc/xtab2/executor3', 'report_req' => { 'report' => uri })
105
105
  result1 = GoodData.post('/gdc/exporter/executor', :result_req => { :format => format, :result => result })
106
- png = GoodData.get(result1['uri'], :process => false)
107
- while png.code == 202
108
- sleep(1)
109
- png = GoodData.get(result1['uri'], :process => false)
110
- end
111
- png
106
+ GoodData.poll_on_code(result1['uri'], process: false)
112
107
  end
113
108
  end
114
109
  end
@@ -108,11 +108,12 @@ module GoodData
108
108
  pull = { 'pullIntegration' => File.basename(dir) }
109
109
  link = project.md.links('etl')['pull']
110
110
  task = GoodData.post link, pull
111
- # TODO: Refactor the task status out
112
- while GoodData.get(task['pullTask']['uri'])['taskStatus'] == 'RUNNING' || GoodData.get(task['pullTask']['uri'])['taskStatus'] == 'PREPARED'
113
- sleep 30
111
+
112
+ res = GoodData.poll_on_response(task['pullTask']['uri']) do |body|
113
+ body['taskStatus'] == 'RUNNING' || body['taskStatus'] == 'PREPARED'
114
114
  end
115
- if GoodData.get(task['pullTask']['uri'])['taskStatus'] == 'ERROR'
115
+
116
+ if res['taskStatus'] == 'ERROR'
116
117
  s = StringIO.new
117
118
  GoodData.download_from_user_webdav(File.basename(dir) + '/upload_status.json', s)
118
119
  js = MultiJson.load(s.string)
@@ -6,6 +6,10 @@ module GoodData
6
6
  class Process
7
7
  attr_reader :data
8
8
 
9
+ alias_method :raw_data, :data
10
+ alias_method :json, :data
11
+ alias_method :to_hash, :data
12
+
9
13
  class << self
10
14
  def [](id, options = {})
11
15
  if id == :all
@@ -27,7 +31,7 @@ module GoodData
27
31
  # TODO: Check the params.
28
32
  def with_deploy(dir, options = {}, &block)
29
33
  # verbose = options[:verbose] || false
30
- GoodData.with_project(options[:project_id]) do |project|
34
+ GoodData.with_project(options[:project_id] || options[:project]) do |project|
31
35
  params = options[:params].nil? ? [] : [options[:params]]
32
36
  if block
33
37
  begin
@@ -71,7 +75,6 @@ module GoodData
71
75
  #
72
76
  # @param path [String] Path to ZIP archive or to a directory containing files that should be ZIPed
73
77
  # @option options [String] :files_to_exclude
74
- # @option options [String] :process_id ('nobody') From address
75
78
  # @option options [String] :type ('GRAPH') Type of process - GRAPH or RUBY
76
79
  # @option options [String] :name Readable name of the process
77
80
  # @option options [String] :process_id ID of a process to be redeployed (do not set if you want to create a new process)
@@ -164,19 +167,12 @@ module GoodData
164
167
  end
165
168
 
166
169
  def schedules
167
- res = []
168
-
169
- scheds = GoodData::Schedule[:all]
170
- scheds['schedules']['items'].each do |item|
171
- if item['schedule']['params']['PROCESS_ID'] == obj_id
172
- res << GoodData::Schedule.new(item)
173
- end
174
- end
175
-
176
- res
170
+ GoodData::Schedule[:all].select { |schedule| schedule.process_id == obj_id }
177
171
  end
178
172
 
179
- alias_method :raw_data, :data
173
+ def create_schedule(cron, executable, options = {})
174
+ GoodData::Schedule.create(process_id, cron, executable, options)
175
+ end
180
176
 
181
177
  def execute(executable, options = {})
182
178
  params = options[:params] || {}
@@ -188,7 +184,7 @@ module GoodData
188
184
  :hiddenParams => hidden_params
189
185
  })
190
186
  begin
191
- GoodData.poll(result, 'executionTask')
187
+ GoodData.poll_on_code(result['executionTask']['links']['poll'])
192
188
  rescue RestClient::RequestFailed => e
193
189
  raise(e)
194
190
  ensure
@@ -119,6 +119,10 @@ module GoodData
119
119
  project
120
120
  end
121
121
 
122
+ def create_from_blueprint(blueprint, options = {})
123
+ GoodData::Model::ProjectCreator.migrate(:spec => blueprint, :token => options[:auth_token])
124
+ end
125
+
122
126
  # Takes one CSV line and creates hash from data extracted
123
127
  #
124
128
  # @param row CSV row
@@ -139,14 +143,16 @@ module GoodData
139
143
 
140
144
  def add_metric(options = {})
141
145
  options[:expression] || fail('Metric has to have its expression defined')
142
- m1 = GoodData::Metric.create(options)
146
+ m1 = GoodData::Metric.xcreate(options)
143
147
  m1.save
144
148
  end
149
+ alias_method :create_metric, :add_metric
145
150
 
146
151
  def add_report(options = {})
147
152
  rep = GoodData::Report.create(options)
148
153
  rep.save
149
154
  end
155
+ alias_method :create_report, :add_report
150
156
 
151
157
  # Returns an indication whether current user is admin in this project
152
158
  #
@@ -160,7 +166,8 @@ module GoodData
160
166
  # @return [GoodData::ProjectRole] Project role if found
161
167
  def blueprint
162
168
  result = GoodData.get("/gdc/projects/#{pid}/model/view")
163
- model = GoodData.poll_on_root(result, 'asyncTask') { |r| r['asyncTask']['link']['poll'] }
169
+ polling_url = result['asyncTask']['link']['poll']
170
+ model = GoodData.poll_on_code(polling_url)
164
171
  GoodData::Model::FromWire.from_wire(model)
165
172
  end
166
173
 
@@ -197,13 +204,10 @@ module GoodData
197
204
 
198
205
  result = GoodData.post("/gdc/md/#{obj_id}/maintenance/export", export)
199
206
  export_token = result['exportArtifact']['token']
200
- status_url = result['exportArtifact']['status']['uri']
201
207
 
202
- state = GoodData.get(status_url)['taskState']['status']
203
- while state == 'RUNNING'
204
- sleep 5
205
- result = GoodData.get(status_url)
206
- state = result['taskState']['status']
208
+ status_url = result['exportArtifact']['status']['uri']
209
+ GoodData.poll_on_response(status_url) do |body|
210
+ body['taskState']['status'] == 'RUNNING'
207
211
  end
208
212
 
209
213
  import = {
@@ -214,12 +218,10 @@ module GoodData
214
218
 
215
219
  result = GoodData.post("/gdc/md/#{new_project.obj_id}/maintenance/import", import)
216
220
  status_url = result['uri']
217
- state = GoodData.get(status_url)['taskState']['status']
218
- while state == 'RUNNING'
219
- sleep 5
220
- result = GoodData.get(status_url)
221
- state = result['taskState']['status']
221
+ GoodData.poll_on_response(status_url) do |body|
222
+ body['taskState']['status'] == 'RUNNING'
222
223
  end
224
+
223
225
  new_project
224
226
  end
225
227
 
@@ -268,6 +270,14 @@ module GoodData
268
270
  end
269
271
  end
270
272
 
273
+ # Helper for getting facts of a project
274
+ #
275
+ # @param [String | Number | Object] Anything that you can pass to GoodData::Fact[id]
276
+ # @return [GoodData::Fact] fact instance or list
277
+ def fact(id)
278
+ GoodData::Fact[id, project: self]
279
+ end
280
+
271
281
  # Gets project role by its identifier
272
282
  #
273
283
  # @param [String] role_name Title of role to look for
@@ -476,9 +486,11 @@ module GoodData
476
486
  polling_url = result['partialMDArtifact']['status']['uri']
477
487
  token = result['partialMDArtifact']['token']
478
488
 
479
- polling_result = GoodData.wait_for_polling_result(polling_url)
489
+ polling_result = GoodData.poll_on_response(polling_url) do |body|
490
+ body['wTaskStatus'] && body['wTaskStatus']['status'] == 'RUNNING'
491
+ end
480
492
 
481
- fail 'Exporting objects failed' if polling_result['wTaskStatus']['status'] == 'ERROR'
493
+ fail 'Exporting objects failed' if polling_result['wTaskStatus'] && polling_result['wTaskStatus']['status'] == 'ERROR'
482
494
 
483
495
  import_payload = {
484
496
  :partialMDImport => {
@@ -490,7 +502,10 @@ module GoodData
490
502
 
491
503
  result = GoodData.post("#{target_project.md['maintenance']}/partialmdimport", import_payload)
492
504
  polling_url = result['uri']
493
- polling_result = GoodData.wait_for_polling_result(polling_url)
505
+
506
+ GoodData.poll_on_response(polling_url) do |body|
507
+ body['wTaskStatus'] && body['wTaskStatus']['status'] == 'RUNNING'
508
+ end
494
509
 
495
510
  fail 'Exporting objects failed' if polling_result['wTaskStatus']['status'] == 'ERROR'
496
511
  end
@@ -576,12 +591,8 @@ module GoodData
576
591
  #
577
592
  # @return [Array<GoodData::Schedule>] List of schedules
578
593
  def schedules
579
- res = []
580
594
  tmp = GoodData.get @json['project']['links']['schedules']
581
- tmp['schedules']['items'].each do |schedule|
582
- res << GoodData::Schedule.new(schedule)
583
- end
584
- res
595
+ tmp['schedules']['items'].map { |schedule| GoodData::Schedule.new(schedule) }
585
596
  end
586
597
 
587
598
  # Gets SLIs data
@@ -791,12 +802,9 @@ module GoodData
791
802
  def validate(filters = %w(ldm pdm metric_filter invalid_objects))
792
803
  response = GoodData.post "#{GoodData.project.md['validate-project']}", 'validateProject' => filters
793
804
  polling_link = response['asyncTask']['link']['poll']
794
- polling_result = GoodData.get(polling_link)
795
- while polling_result['wTaskStatus'] && polling_result['wTaskStatus']['status'] == 'RUNNING'
796
- sleep(3)
797
- polling_result = GoodData.get(polling_link)
805
+ GoodData.poll_on_response(polling_link) do |body|
806
+ body['wTaskStatus'] && body['wTaskStatus']['status'] == 'RUNNING'
798
807
  end
799
- polling_result
800
808
  end
801
809
  end
802
810
  end