gooddata 0.6.11 → 0.6.12
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.
- checksums.yaml +4 -4
- data/.gitignore +6 -0
- data/.travis.yml +5 -0
- data/CHANGELOG.md +34 -1
- data/CLI.md +1 -1
- data/authors.sh +4 -0
- data/lib/gooddata.rb +1 -1
- data/lib/gooddata/cli/commands/api_cmd.rb +0 -2
- data/lib/gooddata/cli/commands/auth_cmd.rb +0 -3
- data/lib/gooddata/cli/commands/console_cmd.rb +1 -2
- data/lib/gooddata/cli/commands/domain_cmd.rb +0 -2
- data/lib/gooddata/cli/commands/process_cmd.rb +0 -2
- data/lib/gooddata/cli/commands/project_cmd.rb +0 -2
- data/lib/gooddata/cli/commands/projects_cmd.rb +0 -2
- data/lib/gooddata/cli/commands/run_ruby_cmd.rb +2 -3
- data/lib/gooddata/cli/commands/scaffold_cmd.rb +0 -3
- data/lib/gooddata/cli/commands/user_cmd.rb +0 -2
- data/lib/gooddata/cli/shared.rb +1 -2
- data/lib/gooddata/commands/datawarehouse.rb +24 -0
- data/lib/gooddata/commands/process.rb +0 -1
- data/lib/gooddata/commands/project.rb +1 -1
- data/lib/gooddata/commands/scaffold.rb +0 -1
- data/lib/gooddata/core/connection.rb +376 -0
- data/lib/gooddata/core/logging.rb +13 -0
- data/lib/gooddata/core/rest.rb +40 -16
- data/lib/gooddata/exceptions/user_in_different_domain.rb +11 -0
- data/lib/gooddata/extensions/enumerable.rb +8 -0
- data/lib/gooddata/goodzilla/goodzilla.rb +24 -0
- data/lib/gooddata/helpers/global_helpers.rb +126 -12
- data/lib/gooddata/mixins/author.rb +11 -5
- data/lib/gooddata/mixins/is_dimension.rb +13 -0
- data/lib/gooddata/mixins/md_object_indexer.rb +17 -1
- data/lib/gooddata/mixins/md_object_query.rb +10 -2
- data/lib/gooddata/mixins/md_relations.rb +2 -2
- data/lib/gooddata/mixins/rest_resource.rb +1 -0
- data/lib/gooddata/models/data_result.rb +0 -1
- data/lib/gooddata/models/datawarehouse.rb +90 -0
- data/lib/gooddata/models/domain.rb +202 -76
- data/lib/gooddata/models/execution.rb +11 -0
- data/lib/gooddata/models/from_wire.rb +4 -4
- data/lib/gooddata/models/invitation.rb +0 -5
- data/lib/gooddata/models/membership.rb +121 -91
- data/lib/gooddata/models/metadata.rb +1 -2
- data/lib/gooddata/models/metadata/attribute.rb +7 -0
- data/lib/gooddata/models/metadata/dashboard.rb +1 -1
- data/lib/gooddata/models/metadata/dimension.rb +52 -0
- data/lib/gooddata/models/metadata/fact.rb +1 -1
- data/lib/gooddata/models/metadata/label.rb +21 -7
- data/lib/gooddata/models/metadata/metric.rb +1 -23
- data/lib/gooddata/models/metadata/report.rb +2 -2
- data/lib/gooddata/models/metadata/report_definition.rb +22 -2
- data/lib/gooddata/models/metadata/variable.rb +81 -0
- data/lib/gooddata/models/model.rb +2 -1
- data/lib/gooddata/models/process.rb +3 -4
- data/lib/gooddata/models/profile.rb +50 -82
- data/lib/gooddata/models/project.rb +170 -213
- data/lib/gooddata/models/project_blueprint.rb +14 -5
- data/lib/gooddata/models/project_creator.rb +2 -2
- data/lib/gooddata/models/schedule.rb +10 -8
- data/lib/gooddata/models/to_wire.rb +2 -2
- data/lib/gooddata/models/user_filters/mandatory_user_filter.rb +67 -0
- data/lib/gooddata/models/user_filters/user_filter.rb +96 -0
- data/lib/gooddata/models/user_filters/user_filter_builder.rb +409 -0
- data/lib/gooddata/{rest/connections/connections.rb → models/user_filters/user_filters.rb} +1 -0
- data/lib/gooddata/models/user_filters/variable_user_filter.rb +14 -0
- data/lib/gooddata/rest/client.rb +32 -21
- data/lib/gooddata/rest/connection.rb +283 -11
- data/lib/gooddata/rest/connections/rest_client_connection.rb +47 -109
- data/lib/gooddata/version.rb +1 -1
- data/spec/data/column_based_permissions.csv +7 -0
- data/spec/data/column_based_permissions2.csv +6 -0
- data/spec/data/hello_world_process/hello_world.rb +3 -1
- data/spec/data/line_based_permissions.csv +3 -0
- data/spec/data/m_n_model/blueprint.json +76 -0
- data/spec/data/{model_view.json → wire_models/model_view.json} +0 -0
- data/spec/data/wire_models/nu_model.json +3046 -0
- data/spec/helpers/process_helper.rb +2 -2
- data/spec/helpers/project_helper.rb +29 -0
- data/spec/helpers/schedule_helper.rb +1 -1
- data/spec/integration/command_datawarehouse_spec.rb +32 -0
- data/spec/integration/create_project_spec.rb +0 -1
- data/spec/integration/full_process_schedule_spec.rb +13 -5
- data/spec/integration/full_project_spec.rb +2 -1
- data/spec/integration/over_to_user_filters_spec.rb +92 -0
- data/spec/integration/project_spec.rb +233 -0
- data/spec/integration/rest_spec.rb +209 -0
- data/spec/integration/user_filters_spec.rb +193 -0
- data/spec/integration/variables_spec.rb +196 -0
- data/spec/unit/commands/command_auth_spec.rb +0 -7
- data/spec/unit/commands/command_process_spec.rb +10 -13
- data/spec/unit/core/connection_spec.rb +0 -19
- data/spec/unit/helpers/global_helpers_spec.rb +57 -0
- data/spec/unit/models/domain_spec.rb +80 -40
- data/spec/unit/models/from_wire_spec.rb +8 -1
- data/spec/unit/models/params_spec.rb +6 -6
- data/spec/unit/models/profile_spec.rb +23 -22
- data/spec/unit/models/project_blueprint_spec.rb +1 -6
- data/spec/unit/models/project_spec.rb +331 -286
- data/spec/unit/models/schedule_spec.rb +39 -14
- data/spec/unit/models/user_filters_spec.rb +89 -0
- data/spec/unit/models/variable_spec.rb +259 -0
- metadata +31 -7
- data/lib/gooddata/rest/connections/dummy_connection.rb +0 -52
- data/spec/unit/core/rest_spec.rb +0 -106
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
|
|
3
|
+
module GoodData
|
|
4
|
+
class VariableUserFilter < UserFilter
|
|
5
|
+
# Creates or updates the variable user filter on the server
|
|
6
|
+
#
|
|
7
|
+
# @return [String]
|
|
8
|
+
def save
|
|
9
|
+
res = client.post(uri, :variable => @json)
|
|
10
|
+
@json['uri'] = res['uri']
|
|
11
|
+
self
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
data/lib/gooddata/rest/client.rb
CHANGED
|
@@ -4,7 +4,7 @@ require 'rest-client'
|
|
|
4
4
|
|
|
5
5
|
require_relative '../helpers/auth_helpers'
|
|
6
6
|
|
|
7
|
-
require_relative '
|
|
7
|
+
require_relative 'connection'
|
|
8
8
|
require_relative 'object_factory'
|
|
9
9
|
|
|
10
10
|
require_relative '../mixins/inspector'
|
|
@@ -19,7 +19,7 @@ module GoodData
|
|
|
19
19
|
#################################
|
|
20
20
|
# Constants
|
|
21
21
|
#################################
|
|
22
|
-
DEFAULT_CONNECTION_IMPLEMENTATION =
|
|
22
|
+
DEFAULT_CONNECTION_IMPLEMENTATION = GoodData::Rest::Connection
|
|
23
23
|
|
|
24
24
|
#################################
|
|
25
25
|
# Class variables
|
|
@@ -60,6 +60,11 @@ module GoodData
|
|
|
60
60
|
# @param password [String] Password to be used for authentication
|
|
61
61
|
# @return [GoodData::Rest::Client] Client
|
|
62
62
|
def connect(username, password, opts = { :verify_ssl => true })
|
|
63
|
+
if username.nil? && password.nil?
|
|
64
|
+
username = ENV['GD_GEM_USER']
|
|
65
|
+
password = ENV['GD_GEM_PASSWORD']
|
|
66
|
+
end
|
|
67
|
+
|
|
63
68
|
new_opts = opts.dup
|
|
64
69
|
if username.is_a?(Hash) && username.key?(:sst_token)
|
|
65
70
|
new_opts[:sst_token] = username[:sst_token]
|
|
@@ -83,7 +88,7 @@ module GoodData
|
|
|
83
88
|
|
|
84
89
|
if client
|
|
85
90
|
at_exit do
|
|
86
|
-
|
|
91
|
+
puts client.connection.stats_table if client && client.connection && (GoodData.stats_on? || client.stats_on?)
|
|
87
92
|
end
|
|
88
93
|
end
|
|
89
94
|
|
|
@@ -104,18 +109,8 @@ module GoodData
|
|
|
104
109
|
end
|
|
105
110
|
|
|
106
111
|
# Retry block if exception thrown
|
|
107
|
-
def retryable(options = {}, &
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
retry_exception, retries = opts[:on], opts[:tries]
|
|
111
|
-
|
|
112
|
-
begin
|
|
113
|
-
return yield
|
|
114
|
-
rescue retry_exception
|
|
115
|
-
retry if (retries -= 1) > 0
|
|
116
|
-
end
|
|
117
|
-
|
|
118
|
-
yield
|
|
112
|
+
def retryable(options = {}, &block)
|
|
113
|
+
GoodData::Rest::Connection.retryable(options, &block)
|
|
119
114
|
end
|
|
120
115
|
|
|
121
116
|
alias_method :client, :connection
|
|
@@ -145,7 +140,7 @@ module GoodData
|
|
|
145
140
|
end
|
|
146
141
|
|
|
147
142
|
def create_project(options = { title: 'Project', auth_token: ENV['GD_PROJECT_TOKEN'] })
|
|
148
|
-
GoodData::Project.create(
|
|
143
|
+
GoodData::Project.create({ client: self }.merge(options))
|
|
149
144
|
end
|
|
150
145
|
|
|
151
146
|
def create_project_from_blueprint(blueprint, options = {})
|
|
@@ -192,8 +187,24 @@ module GoodData
|
|
|
192
187
|
nil
|
|
193
188
|
end
|
|
194
189
|
|
|
195
|
-
def user
|
|
196
|
-
|
|
190
|
+
def user(id = nil)
|
|
191
|
+
if id
|
|
192
|
+
create(GoodData::Profile, get(id))
|
|
193
|
+
else
|
|
194
|
+
create(GoodData::Profile, @connection.user)
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def stats_off
|
|
199
|
+
@stats = false
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def stats_on
|
|
203
|
+
@stats = true
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def stats_on? # rubocop:disable Style/TrivialAccessors
|
|
207
|
+
@stats
|
|
197
208
|
end
|
|
198
209
|
|
|
199
210
|
#######################
|
|
@@ -253,7 +264,7 @@ module GoodData
|
|
|
253
264
|
|
|
254
265
|
while response.code == code
|
|
255
266
|
sleep sleep_interval
|
|
256
|
-
GoodData::Rest::Client.retryable(:tries => 3, :
|
|
267
|
+
GoodData::Rest::Client.retryable(:tries => 3, :refresh_token => proc { connection.refresh_token }) do
|
|
257
268
|
sleep sleep_interval
|
|
258
269
|
response = get(link, :process => false)
|
|
259
270
|
end
|
|
@@ -279,7 +290,7 @@ module GoodData
|
|
|
279
290
|
response = get(link)
|
|
280
291
|
while bl.call(response)
|
|
281
292
|
sleep sleep_interval
|
|
282
|
-
GoodData::Rest::Client.retryable(:tries => 3, :
|
|
293
|
+
GoodData::Rest::Client.retryable(:tries => 3, :refresh_token => proc { connection.refresh_token }) do
|
|
283
294
|
sleep sleep_interval
|
|
284
295
|
response = get(link)
|
|
285
296
|
end
|
|
@@ -318,7 +329,7 @@ module GoodData
|
|
|
318
329
|
@connection.download source_relative_path, target_file_path, options
|
|
319
330
|
end
|
|
320
331
|
|
|
321
|
-
def download_from_user_webdav(source_relative_path, target_file_path, options = {})
|
|
332
|
+
def download_from_user_webdav(source_relative_path, target_file_path, options = { :client => GoodData.client, :project => project })
|
|
322
333
|
download(source_relative_path, target_file_path, options.merge(
|
|
323
334
|
:directory => options[:directory],
|
|
324
335
|
:staging_url => get_user_webdav_url(options)
|
|
@@ -23,6 +23,16 @@ module GoodData
|
|
|
23
23
|
:headers => DEFAULT_HEADERS
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
RETRYABLE_ERRORS = [
|
|
27
|
+
RestClient::InternalServerError,
|
|
28
|
+
RestClient::RequestTimeout,
|
|
29
|
+
RestClient::MethodNotAllowed,
|
|
30
|
+
SystemCallError,
|
|
31
|
+
Timeout::Error
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
RETRYABLE_ERRORS << Net::ReadTimeout if Net.const_defined?(:ReadTimeout)
|
|
35
|
+
|
|
26
36
|
class << self
|
|
27
37
|
def construct_login_payload(username, password)
|
|
28
38
|
res = {
|
|
@@ -34,6 +44,36 @@ module GoodData
|
|
|
34
44
|
}
|
|
35
45
|
res
|
|
36
46
|
end
|
|
47
|
+
|
|
48
|
+
# Retry block if exception thrown
|
|
49
|
+
def retryable(options = {}, &_block)
|
|
50
|
+
opts = { :tries => 1, :on => RETRYABLE_ERRORS }.merge(options)
|
|
51
|
+
|
|
52
|
+
retry_exception, retries = opts[:on], opts[:tries]
|
|
53
|
+
|
|
54
|
+
unless retry_exception.is_a?(Array)
|
|
55
|
+
retry_exception = [retry_exception]
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
retry_time = 1
|
|
59
|
+
begin
|
|
60
|
+
return yield
|
|
61
|
+
rescue RestClient::Forbidden => e # , RestClient::Unauthorized => e
|
|
62
|
+
raise e unless options[:refresh_token]
|
|
63
|
+
options[:refresh_token].call
|
|
64
|
+
retry if (retries -= 1) > 0
|
|
65
|
+
rescue RestClient::TooManyRequests
|
|
66
|
+
GoodData.logger.warn "Too many requests, retrying in #{retry_time} seconds"
|
|
67
|
+
sleep retry_time
|
|
68
|
+
retry_time *= 1.5
|
|
69
|
+
retry
|
|
70
|
+
rescue *retry_exception => e
|
|
71
|
+
GoodData.logger.warn e.inspect
|
|
72
|
+
retry if (retries -= 1) > 0
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
yield
|
|
76
|
+
end
|
|
37
77
|
end
|
|
38
78
|
|
|
39
79
|
attr_reader :cookies
|
|
@@ -41,17 +81,39 @@ module GoodData
|
|
|
41
81
|
attr_reader :user
|
|
42
82
|
|
|
43
83
|
def initialize(opts)
|
|
84
|
+
@stats = {}
|
|
85
|
+
@opts = opts
|
|
86
|
+
|
|
87
|
+
@headers = DEFAULT_HEADERS.dup
|
|
44
88
|
@user = nil
|
|
89
|
+
@server = nil
|
|
45
90
|
|
|
46
|
-
@stats = {}
|
|
47
91
|
@opts = opts
|
|
92
|
+
headers = opts[:headers] || {}
|
|
93
|
+
@headers.merge! headers
|
|
48
94
|
|
|
49
95
|
# Initialize cookies
|
|
50
96
|
reset_cookies!
|
|
97
|
+
|
|
98
|
+
@at_exit_handler_installed = nil
|
|
51
99
|
end
|
|
52
100
|
|
|
53
101
|
# Connect using username and password
|
|
54
102
|
def connect(username, password, options = {})
|
|
103
|
+
server = options[:server] || DEFAULT_URL
|
|
104
|
+
@server = RestClient::Resource.new server, DEFAULT_LOGIN_PAYLOAD
|
|
105
|
+
|
|
106
|
+
# Install at_exit handler first
|
|
107
|
+
unless @at_exit_handler_installed
|
|
108
|
+
begin
|
|
109
|
+
at_exit { disconnect if @user }
|
|
110
|
+
rescue RestClient::Unauthorized
|
|
111
|
+
GoodData.logger.info 'Already logged out'
|
|
112
|
+
ensure
|
|
113
|
+
@at_exit_handler_installed = true
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
55
117
|
# Reset old cookies first
|
|
56
118
|
if options[:sst_token]
|
|
57
119
|
merge_cookies!('GDCAuthSST' => options[:sst_token])
|
|
@@ -62,8 +124,8 @@ module GoodData
|
|
|
62
124
|
credentials = Connection.construct_login_payload(username, password)
|
|
63
125
|
@auth = post(LOGIN_PATH, credentials, :dont_reauth => true)['userLogin']
|
|
64
126
|
|
|
65
|
-
@user = get(@auth['profile'])
|
|
66
127
|
refresh_token :dont_reauth => true
|
|
128
|
+
@user = get(@auth['profile'])
|
|
67
129
|
end
|
|
68
130
|
end
|
|
69
131
|
|
|
@@ -71,7 +133,12 @@ module GoodData
|
|
|
71
133
|
def disconnect
|
|
72
134
|
# TODO: Wrap somehow
|
|
73
135
|
url = @auth['state']
|
|
74
|
-
|
|
136
|
+
|
|
137
|
+
begin
|
|
138
|
+
delete url if url
|
|
139
|
+
rescue RestClient::Unauthorized
|
|
140
|
+
GoodData.logger.info 'Already disconnected'
|
|
141
|
+
end
|
|
75
142
|
|
|
76
143
|
@auth = nil
|
|
77
144
|
@server = nil
|
|
@@ -80,6 +147,72 @@ module GoodData
|
|
|
80
147
|
reset_cookies!
|
|
81
148
|
end
|
|
82
149
|
|
|
150
|
+
def download(what, where, options = {})
|
|
151
|
+
# handle the path (directory) given in what
|
|
152
|
+
ilast_slash = what.rindex('/')
|
|
153
|
+
if ilast_slash.nil?
|
|
154
|
+
what_dir = ''
|
|
155
|
+
else
|
|
156
|
+
# take the directory from the path
|
|
157
|
+
what_dir = what[0..ilast_slash - 1]
|
|
158
|
+
# take the filename from the path
|
|
159
|
+
what = what[ilast_slash + 1..-1]
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
option_dir = options[:directory] || ''
|
|
163
|
+
option_dir = option_dir[0..-2] if option_dir[-1] == '/'
|
|
164
|
+
|
|
165
|
+
# join the otion dir with the what_dir
|
|
166
|
+
# [option dir empty, what dir empty] => the joined dir
|
|
167
|
+
dir_hash = {
|
|
168
|
+
[true, true] => '',
|
|
169
|
+
[true, false] => what_dir,
|
|
170
|
+
[false, true] => option_dir,
|
|
171
|
+
[false, false] => "#{what_dir}/#{option_dir}"
|
|
172
|
+
}
|
|
173
|
+
dir = dir_hash[[option_dir.empty?, what_dir.empty?]]
|
|
174
|
+
|
|
175
|
+
staging_uri = options[:staging_url].to_s
|
|
176
|
+
|
|
177
|
+
base_url = dir.empty? ? staging_uri : URI.join(staging_uri, "#{dir}/").to_s
|
|
178
|
+
url = URI.join(base_url, CGI.escape(what)).to_s
|
|
179
|
+
|
|
180
|
+
b = proc do
|
|
181
|
+
raw = {
|
|
182
|
+
:headers => {
|
|
183
|
+
:user_agent => GoodData.gem_version_string
|
|
184
|
+
},
|
|
185
|
+
:method => :get,
|
|
186
|
+
:url => url
|
|
187
|
+
}.merge(cookies)
|
|
188
|
+
|
|
189
|
+
if where.is_a?(IO)
|
|
190
|
+
RestClient::Request.execute(raw) do |chunk, _x, response|
|
|
191
|
+
if response.code.to_s != '200'
|
|
192
|
+
fail ArgumentError, "Error downloading #{url}. Got response: #{response.code} #{response} #{response.body}"
|
|
193
|
+
end
|
|
194
|
+
where.write chunk
|
|
195
|
+
end
|
|
196
|
+
else
|
|
197
|
+
# Assume it is a string or file
|
|
198
|
+
File.open(where, 'w') do |f|
|
|
199
|
+
RestClient::Request.execute(raw) do |chunk, _x, response|
|
|
200
|
+
if response.code.to_s != '200'
|
|
201
|
+
fail ArgumentError, "Error downloading #{url}. Got response: #{response.code} #{response} #{response.body}"
|
|
202
|
+
end
|
|
203
|
+
f.write chunk
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
res = nil
|
|
210
|
+
GoodData::Rest::Connection.retryable(:tries => 2, :refresh_token => proc { refresh_token }) do
|
|
211
|
+
res = b.call
|
|
212
|
+
end
|
|
213
|
+
res
|
|
214
|
+
end
|
|
215
|
+
|
|
83
216
|
def refresh_token(_options = {})
|
|
84
217
|
begin # rubocop:disable RedundantBegin
|
|
85
218
|
get TOKEN_PATH, :dont_reauth => true # avoid infinite loop GET fails with 401
|
|
@@ -99,29 +232,47 @@ module GoodData
|
|
|
99
232
|
# HTTP DELETE
|
|
100
233
|
#
|
|
101
234
|
# @param uri [String] Target URI
|
|
102
|
-
def delete(uri,
|
|
103
|
-
|
|
235
|
+
def delete(uri, options = {})
|
|
236
|
+
GoodData.logger.debug "DELETE: #{@server.url}#{uri}"
|
|
237
|
+
profile "DELETE #{uri}" do
|
|
238
|
+
b = proc { @server[uri].delete cookies }
|
|
239
|
+
process_response(options, &b)
|
|
240
|
+
end
|
|
104
241
|
end
|
|
105
242
|
|
|
106
243
|
# HTTP GET
|
|
107
244
|
#
|
|
108
245
|
# @param uri [String] Target URI
|
|
109
|
-
def get(uri,
|
|
110
|
-
|
|
246
|
+
def get(uri, options = {}, &user_block)
|
|
247
|
+
GoodData.logger.debug "GET: #{@server.url}#{uri}"
|
|
248
|
+
profile "GET #{uri}" do
|
|
249
|
+
b = proc { @server[uri].get(cookies, &user_block) }
|
|
250
|
+
process_response(options, &b)
|
|
251
|
+
end
|
|
111
252
|
end
|
|
112
253
|
|
|
113
254
|
# HTTP PUT
|
|
114
255
|
#
|
|
115
256
|
# @param uri [String] Target URI
|
|
116
|
-
def put(uri,
|
|
117
|
-
|
|
257
|
+
def put(uri, data, options = {})
|
|
258
|
+
payload = data.is_a?(Hash) ? data.to_json : data
|
|
259
|
+
GoodData.logger.debug "PUT: #{@server.url}#{uri}, #{scrub_params(data, [:password, :login, :authorizationToken])}"
|
|
260
|
+
profile "PUT #{uri}" do
|
|
261
|
+
b = proc { @server[uri].put payload, cookies }
|
|
262
|
+
process_response(options, &b)
|
|
263
|
+
end
|
|
118
264
|
end
|
|
119
265
|
|
|
120
266
|
# HTTP POST
|
|
121
267
|
#
|
|
122
268
|
# @param uri [String] Target URI
|
|
123
|
-
def post(uri,
|
|
124
|
-
|
|
269
|
+
def post(uri, data, options = {})
|
|
270
|
+
GoodData.logger.debug "POST: #{@server.url}#{uri}, #{scrub_params(data, [:password, :login, :authorizationToken])}"
|
|
271
|
+
profile "POST #{uri}" do
|
|
272
|
+
payload = data.is_a?(Hash) ? data.to_json : data
|
|
273
|
+
b = proc { @server[uri].post payload, cookies }
|
|
274
|
+
process_response(options, &b)
|
|
275
|
+
end
|
|
125
276
|
end
|
|
126
277
|
|
|
127
278
|
# Reader method for SST token
|
|
@@ -155,12 +306,133 @@ module GoodData
|
|
|
155
306
|
cookies[:cookies]['GDCAuthTT']
|
|
156
307
|
end
|
|
157
308
|
|
|
309
|
+
# Uploads a file to GoodData server
|
|
310
|
+
def upload(file, options = {})
|
|
311
|
+
def do_stream_file(uri, filename, _options = {})
|
|
312
|
+
puts "uploading the file #{uri}"
|
|
313
|
+
|
|
314
|
+
to_upload = File.new(filename)
|
|
315
|
+
cookies_str = cookies[:cookies].map { |cookie| "#{cookie[0]}=#{cookie[1]}" }.join(';')
|
|
316
|
+
req = Net::HTTP::Put.new(uri.path, 'User-Agent' => GoodData.gem_version_string, 'Cookie' => cookies_str)
|
|
317
|
+
req.content_length = to_upload.size
|
|
318
|
+
req.body_stream = to_upload
|
|
319
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
320
|
+
http.use_ssl = true
|
|
321
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
|
322
|
+
|
|
323
|
+
response = nil
|
|
324
|
+
GoodData::Rest::Connection.retryable(:tries => 2, :refresh_token => proc { refresh_token }) do
|
|
325
|
+
response = http.start { |client| client.request(req) }
|
|
326
|
+
end
|
|
327
|
+
response
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
def webdav_dir_exists?(url)
|
|
331
|
+
method = :get
|
|
332
|
+
GoodData.logger.debug "#{method}: #{url}"
|
|
333
|
+
|
|
334
|
+
b = proc do
|
|
335
|
+
raw = {
|
|
336
|
+
:method => method,
|
|
337
|
+
:url => url,
|
|
338
|
+
:headers => @headers
|
|
339
|
+
}
|
|
340
|
+
begin
|
|
341
|
+
RestClient::Request.execute(raw.merge(cookies))
|
|
342
|
+
rescue RestClient::Exception => e
|
|
343
|
+
false if e.http_code == 404
|
|
344
|
+
end
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
res = nil
|
|
348
|
+
GoodData::Rest::Connection.retryable(:tries => 2, :refresh_token => proc { refresh_token }) do
|
|
349
|
+
res = b.call
|
|
350
|
+
end
|
|
351
|
+
res
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
def create_webdav_dir_if_needed(url)
|
|
355
|
+
return if webdav_dir_exists?(url)
|
|
356
|
+
|
|
357
|
+
method = :mkcol
|
|
358
|
+
GoodData.logger.debug "#{method}: #{url}"
|
|
359
|
+
b = proc do
|
|
360
|
+
raw = {
|
|
361
|
+
:method => method,
|
|
362
|
+
:url => url,
|
|
363
|
+
:headers => @headers
|
|
364
|
+
}.merge(cookies)
|
|
365
|
+
RestClient::Request.execute(raw)
|
|
366
|
+
end
|
|
367
|
+
|
|
368
|
+
GoodData::Rest::Connection.retryable(:tries => 2, :refresh_token => proc { refresh_token }) do
|
|
369
|
+
b.call
|
|
370
|
+
end
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
dir = options[:directory] || ''
|
|
374
|
+
staging_uri = options[:staging_url].to_s
|
|
375
|
+
url = dir.empty? ? staging_uri : URI.join(staging_uri, "#{dir}/").to_s
|
|
376
|
+
|
|
377
|
+
# Make a directory, if needed
|
|
378
|
+
create_webdav_dir_if_needed url unless dir.empty?
|
|
379
|
+
|
|
380
|
+
webdav_filename = options[:filename] || File.basename(file)
|
|
381
|
+
do_stream_file URI.join(url, CGI.escape(webdav_filename)), file
|
|
382
|
+
end
|
|
383
|
+
|
|
158
384
|
private
|
|
159
385
|
|
|
160
386
|
def merge_cookies!(cookies)
|
|
161
387
|
@cookies[:cookies].merge! cookies
|
|
162
388
|
end
|
|
163
389
|
|
|
390
|
+
def process_response(options = {}, &block)
|
|
391
|
+
# begin
|
|
392
|
+
# # Simply try again when ConnectionReset, ConnectionRefused etc.. (see e.g. MSF-7591)
|
|
393
|
+
# response = GoodData::Rest::Connection.retryable(:tries => 2, :refresh_token => Proc.new { refresh_token }) do
|
|
394
|
+
# block.call
|
|
395
|
+
# end
|
|
396
|
+
# rescue RestClient::Unauthorized
|
|
397
|
+
# raise $ERROR_INFO if options[:dont_reauth]
|
|
398
|
+
# GoodData::Rest::Connection.retryable(:tries => 2) do
|
|
399
|
+
# refresh_token
|
|
400
|
+
# response = block.call
|
|
401
|
+
# end
|
|
402
|
+
# end
|
|
403
|
+
|
|
404
|
+
response = GoodData::Rest::Connection.retryable(:tries => 2, :refresh_token => proc { refresh_token }) do
|
|
405
|
+
block.call
|
|
406
|
+
end
|
|
407
|
+
|
|
408
|
+
merge_cookies! response.cookies
|
|
409
|
+
content_type = response.headers[:content_type]
|
|
410
|
+
return response if options[:process] == false
|
|
411
|
+
|
|
412
|
+
if content_type == 'application/json' || content_type == 'application/json;charset=UTF-8'
|
|
413
|
+
result = response.to_str == '""' ? {} : MultiJson.load(response.to_str)
|
|
414
|
+
GoodData.logger.debug "Request ID: #{response.headers[:x_gdc_request]} - Response: #{result.inspect}"
|
|
415
|
+
elsif ['text/plain;charset=UTF-8', 'text/plain; charset=UTF-8', 'text/plain'].include?(content_type)
|
|
416
|
+
result = response
|
|
417
|
+
GoodData.logger.debug 'Response: plain text'
|
|
418
|
+
elsif content_type == 'application/zip'
|
|
419
|
+
result = response
|
|
420
|
+
GoodData.logger.debug 'Response: a zipped stream'
|
|
421
|
+
elsif response.headers[:content_length].to_s == '0'
|
|
422
|
+
result = nil
|
|
423
|
+
GoodData.logger.debug 'Response: Empty response possibly 204'
|
|
424
|
+
elsif response.code == 204
|
|
425
|
+
result = nil
|
|
426
|
+
GoodData.logger.debug 'Response: 204 no content'
|
|
427
|
+
else
|
|
428
|
+
fail "Unsupported response content type '%s':\n%s" % [content_type, response.to_str[0..127]]
|
|
429
|
+
end
|
|
430
|
+
result
|
|
431
|
+
rescue RestClient::Exception => e
|
|
432
|
+
GoodData.logger.debug "Response: #{e.response}"
|
|
433
|
+
raise $ERROR_INFO
|
|
434
|
+
end
|
|
435
|
+
|
|
164
436
|
def profile(title, &block)
|
|
165
437
|
t1 = Time.now
|
|
166
438
|
res = block.call
|