gooddata 0.6.12 → 0.6.13
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/.travis.yml +11 -12
- data/CHANGELOG.md +10 -1
- data/gooddata.gemspec +3 -3
- data/lib/gooddata/commands/datawarehouse.rb +4 -6
- data/lib/gooddata/core/rest.rb +1 -3
- data/lib/gooddata/models/domain.rb +1 -1
- data/lib/gooddata/models/from_wire.rb +4 -4
- data/lib/gooddata/models/invitation.rb +0 -4
- data/lib/gooddata/models/membership.rb +0 -7
- data/lib/gooddata/models/metadata/attribute.rb +29 -1
- data/lib/gooddata/models/metadata/dataset.rb +62 -0
- data/lib/gooddata/models/metadata/metric.rb +16 -4
- data/lib/gooddata/models/metadata/report_definition.rb +2 -0
- data/lib/gooddata/models/process.rb +1 -1
- data/lib/gooddata/models/project.rb +12 -9
- data/lib/gooddata/models/project_blueprint.rb +3 -14
- data/lib/gooddata/models/user_filters/variable_user_filter.rb +2 -0
- data/lib/gooddata/rest/client.rb +4 -8
- data/lib/gooddata/rest/connection.rb +7 -5
- data/lib/gooddata/version.rb +1 -1
- data/spec/helpers/project_helper.rb +1 -1
- data/spec/integration/full_process_schedule_spec.rb +83 -42
- data/spec/integration/full_project_spec.rb +12 -0
- data/spec/logging_in_logging_out_spec.rb +8 -0
- data/spec/spec_helper.rb +2 -0
- data/spec/unit/models/domain_spec.rb +2 -1
- data/spec/unit/models/params_spec.rb +36 -18
- data/spec/unit/models/project_role_spec.rb +8 -7
- data/spec/unit/models/schedule_spec.rb +267 -290
- metadata +32 -4
- data/lib/gooddata/core/connection.rb +0 -376
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gooddata
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.6.
|
4
|
+
version: 0.6.13
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Pavel Kolesnikov
|
@@ -11,7 +11,7 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date: 2015-
|
14
|
+
date: 2015-02-09 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: rake
|
@@ -201,6 +201,34 @@ dependencies:
|
|
201
201
|
- - ">="
|
202
202
|
- !ruby/object:Gem::Version
|
203
203
|
version: 0.7.0
|
204
|
+
- !ruby/object:Gem::Dependency
|
205
|
+
name: debase
|
206
|
+
requirement: !ruby/object:Gem::Requirement
|
207
|
+
requirements:
|
208
|
+
- - ">="
|
209
|
+
- !ruby/object:Gem::Version
|
210
|
+
version: '0'
|
211
|
+
type: :development
|
212
|
+
prerelease: false
|
213
|
+
version_requirements: !ruby/object:Gem::Requirement
|
214
|
+
requirements:
|
215
|
+
- - ">="
|
216
|
+
- !ruby/object:Gem::Version
|
217
|
+
version: '0'
|
218
|
+
- !ruby/object:Gem::Dependency
|
219
|
+
name: ruby-debug-ide
|
220
|
+
requirement: !ruby/object:Gem::Requirement
|
221
|
+
requirements:
|
222
|
+
- - ">="
|
223
|
+
- !ruby/object:Gem::Version
|
224
|
+
version: '0'
|
225
|
+
type: :development
|
226
|
+
prerelease: false
|
227
|
+
version_requirements: !ruby/object:Gem::Requirement
|
228
|
+
requirements:
|
229
|
+
- - ">="
|
230
|
+
- !ruby/object:Gem::Version
|
231
|
+
version: '0'
|
204
232
|
- !ruby/object:Gem::Dependency
|
205
233
|
name: activesupport
|
206
234
|
requirement: !ruby/object:Gem::Requirement
|
@@ -621,7 +649,6 @@ files:
|
|
621
649
|
- lib/gooddata/commands/scaffold.rb
|
622
650
|
- lib/gooddata/commands/user.rb
|
623
651
|
- lib/gooddata/connection.rb
|
624
|
-
- lib/gooddata/core/connection.rb
|
625
652
|
- lib/gooddata/core/core.rb
|
626
653
|
- lib/gooddata/core/logging.rb
|
627
654
|
- lib/gooddata/core/nil_logger.rb
|
@@ -698,6 +725,7 @@ files:
|
|
698
725
|
- lib/gooddata/models/metadata.rb
|
699
726
|
- lib/gooddata/models/metadata/attribute.rb
|
700
727
|
- lib/gooddata/models/metadata/dashboard.rb
|
728
|
+
- lib/gooddata/models/metadata/dataset.rb
|
701
729
|
- lib/gooddata/models/metadata/dimension.rb
|
702
730
|
- lib/gooddata/models/metadata/fact.rb
|
703
731
|
- lib/gooddata/models/metadata/label.rb
|
@@ -881,7 +909,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
881
909
|
version: '0'
|
882
910
|
requirements: []
|
883
911
|
rubyforge_project:
|
884
|
-
rubygems_version: 2.
|
912
|
+
rubygems_version: 2.1.9
|
885
913
|
signing_key:
|
886
914
|
specification_version: 4
|
887
915
|
summary: A convenient Ruby wrapper around the GoodData RESTful API
|
@@ -1,376 +0,0 @@
|
|
1
|
-
# encoding: UTF-8
|
2
|
-
|
3
|
-
require 'multi_json'
|
4
|
-
require 'rest-client'
|
5
|
-
|
6
|
-
require_relative '../version'
|
7
|
-
|
8
|
-
module GoodData
|
9
|
-
# # GoodData HTTP wrapper
|
10
|
-
#
|
11
|
-
# Provides a convenient HTTP wrapper for talking with the GoodData API.
|
12
|
-
#
|
13
|
-
# Remember that the connection is shared amongst the entire application.
|
14
|
-
# Therefore you can't be logged in to more than _one_ GoodData account.
|
15
|
-
# per session. Simultaneous connections to multiple GoodData accounts is not
|
16
|
-
# supported at this time.
|
17
|
-
#
|
18
|
-
# The GoodData API is a RESTful API that communicates using JSON. This wrapper
|
19
|
-
# makes sure that the session is stored between requests and that the JSON is
|
20
|
-
# parsed both when sending and receiving.
|
21
|
-
#
|
22
|
-
# ## Usage
|
23
|
-
#
|
24
|
-
# Before a connection can be made to the GoodData API, you have to supply the user credentials like this:
|
25
|
-
#
|
26
|
-
# Connection.new(username, password)
|
27
|
-
#
|
28
|
-
# To send a HTTP request use either the get, post or delete methods documented below.
|
29
|
-
#
|
30
|
-
class Connection
|
31
|
-
DEFAULT_URL = 'https://secure.gooddata.com'
|
32
|
-
LOGIN_PATH = '/gdc/account/login'
|
33
|
-
TOKEN_PATH = '/gdc/account/token'
|
34
|
-
|
35
|
-
attr_reader(:auth_token, :url)
|
36
|
-
attr_accessor :status, :options
|
37
|
-
|
38
|
-
# Options:
|
39
|
-
# * :tries - Number of retries to perform. Defaults to 1.
|
40
|
-
# * :on - The Exception on which a retry will be performed. Defaults to Exception, which retries on any Exception.
|
41
|
-
#
|
42
|
-
# ### Example
|
43
|
-
#
|
44
|
-
# retryable(:tries => 1, :on => OpenURI::HTTPError) do
|
45
|
-
# # your code here
|
46
|
-
# end
|
47
|
-
#
|
48
|
-
def retryable(options = {})
|
49
|
-
opts = { :tries => 1, :on => Exception }.merge(options)
|
50
|
-
|
51
|
-
retry_exception, retries = opts[:on], opts[:tries]
|
52
|
-
|
53
|
-
begin
|
54
|
-
return yield
|
55
|
-
rescue retry_exception
|
56
|
-
retry if (retries -= 1) > 0
|
57
|
-
end
|
58
|
-
|
59
|
-
yield
|
60
|
-
end
|
61
|
-
|
62
|
-
# Set the GoodData account credentials.
|
63
|
-
#
|
64
|
-
# This have to be performed before any calls to the API.
|
65
|
-
#
|
66
|
-
# @param username The GoodData account username
|
67
|
-
# @param password The GoodData account password
|
68
|
-
#
|
69
|
-
def initialize(username, password, options = {})
|
70
|
-
@status = :not_connected
|
71
|
-
@username = username
|
72
|
-
@password = password
|
73
|
-
@url = options[:server] || DEFAULT_URL
|
74
|
-
@auth_token = options[:gdc_temporary_token]
|
75
|
-
@options = options
|
76
|
-
|
77
|
-
@headers = options[:headers] || {}
|
78
|
-
|
79
|
-
default_headers = {
|
80
|
-
:content_type => :json,
|
81
|
-
:accept => [:json, :zip],
|
82
|
-
:user_agent => GoodData.gem_version_string
|
83
|
-
}
|
84
|
-
default_headers.merge! @headers
|
85
|
-
|
86
|
-
@server = RestClient::Resource.new @url,
|
87
|
-
:timeout => @options[:timeout],
|
88
|
-
:headers => default_headers
|
89
|
-
|
90
|
-
@server = create_server_connection(@url, @options)
|
91
|
-
end
|
92
|
-
|
93
|
-
# Returns the user JSON object of the currently logged in GoodData user account.
|
94
|
-
def user
|
95
|
-
ensure_connection
|
96
|
-
@user
|
97
|
-
end
|
98
|
-
|
99
|
-
# Performs a HTTP GET request.
|
100
|
-
#
|
101
|
-
# Retuns the JSON response formatted as a Hash object.
|
102
|
-
#
|
103
|
-
# @param path The HTTP path on the GoodData server (must be prefixed with a forward slash)
|
104
|
-
#
|
105
|
-
# ### Examples
|
106
|
-
#
|
107
|
-
# Connection.new(username, password).get '/gdc/projects'
|
108
|
-
#
|
109
|
-
def get(path, options = {})
|
110
|
-
GoodData.logger.debug "GET #{@server}#{path}"
|
111
|
-
ensure_connection
|
112
|
-
b = proc { @server[path].get cookies }
|
113
|
-
process_response(options, &b)
|
114
|
-
end
|
115
|
-
|
116
|
-
# Performs a HTTP POST request.
|
117
|
-
#
|
118
|
-
# Retuns the JSON response formatted as a Hash object.
|
119
|
-
#
|
120
|
-
# @param path The HTTP path on the GoodData server (must be prefixed with a forward slash)
|
121
|
-
# @param data The payload data in the format of a Hash object
|
122
|
-
#
|
123
|
-
# ### Examples
|
124
|
-
#
|
125
|
-
# Connection.new(username, password).post '/gdc/projects', { ... }
|
126
|
-
#
|
127
|
-
def post(path, data, options = {})
|
128
|
-
GoodData.logger.debug("POST #{@server}#{path}, payload: #{scrub_params(data, [:password, :login, :authorizationToken])}")
|
129
|
-
ensure_connection
|
130
|
-
payload = data.is_a?(Hash) ? data.to_json : data
|
131
|
-
b = proc { @server[path].post payload, cookies }
|
132
|
-
process_response(options, &b)
|
133
|
-
end
|
134
|
-
|
135
|
-
# Performs a HTTP PUT request.
|
136
|
-
#
|
137
|
-
# Retuns the JSON response formatted as a Hash object.
|
138
|
-
#
|
139
|
-
# @param path The HTTP path on the GoodData server (must be prefixed with a forward slash)
|
140
|
-
# @param data The payload data in the format of a Hash object
|
141
|
-
#
|
142
|
-
# ### Examples
|
143
|
-
#
|
144
|
-
# Connection.new(username, password).put '/gdc/projects', { ... }
|
145
|
-
#
|
146
|
-
def put(path, data, options = {})
|
147
|
-
payload = data.is_a?(Hash) ? data.to_json : data
|
148
|
-
GoodData.logger.debug "PUT #{@server}#{path}, payload: #{payload}"
|
149
|
-
ensure_connection
|
150
|
-
b = proc { @server[path].put payload, cookies }
|
151
|
-
process_response(options, &b)
|
152
|
-
end
|
153
|
-
|
154
|
-
# Performs a HTTP DELETE request.
|
155
|
-
#
|
156
|
-
# Retuns the JSON response formatted as a Hash object.
|
157
|
-
#
|
158
|
-
# @param path The HTTP path on the GoodData server (must be prefixed with a forward slash)
|
159
|
-
#
|
160
|
-
# ### Examples
|
161
|
-
#
|
162
|
-
# Connection.new(username, password).delete '/gdc/project/1'
|
163
|
-
#
|
164
|
-
def delete(path, options = {})
|
165
|
-
GoodData.logger.debug "DELETE #{@server}#{path}"
|
166
|
-
ensure_connection
|
167
|
-
b = proc { @server[path].delete cookies }
|
168
|
-
process_response(options, &b)
|
169
|
-
end
|
170
|
-
|
171
|
-
# Get the cookies associated with the current connection.
|
172
|
-
def cookies
|
173
|
-
@cookies ||= { :cookies => {} }
|
174
|
-
end
|
175
|
-
|
176
|
-
# Set the cookies used when communicating with the GoodData API.
|
177
|
-
def merge_cookies!(cookies)
|
178
|
-
self.cookies
|
179
|
-
@cookies[:cookies].merge! cookies
|
180
|
-
end
|
181
|
-
|
182
|
-
# Returns true if a connection have been established to the GoodData API
|
183
|
-
# and the login was successful.
|
184
|
-
def logged_in?
|
185
|
-
@status == :logged_in
|
186
|
-
end
|
187
|
-
|
188
|
-
def url=(url = nil)
|
189
|
-
@url = url || DEFAULT_URL
|
190
|
-
@server = create_server_connection(@url, @options)
|
191
|
-
end
|
192
|
-
|
193
|
-
# The connection will automatically be established once it's needed, which it
|
194
|
-
# usually is when either the user, get, post or delete method is called. If you
|
195
|
-
# want to force a connection (or a re-connect) you can use this method.
|
196
|
-
def connect!
|
197
|
-
connect
|
198
|
-
end
|
199
|
-
|
200
|
-
# Uploads a file to GoodData server
|
201
|
-
# /uploads/ resources are special in that they use a different
|
202
|
-
# host and a basic authentication.
|
203
|
-
def upload(file, options = {})
|
204
|
-
ensure_connection
|
205
|
-
|
206
|
-
dir = options[:directory] || ''
|
207
|
-
staging_uri = options[:staging_url].to_s
|
208
|
-
url = dir.empty? ? staging_uri : URI.join(staging_uri, "#{dir}/").to_s
|
209
|
-
|
210
|
-
# Make a directory, if needed
|
211
|
-
unless dir.empty?
|
212
|
-
method = :get
|
213
|
-
GoodData.logger.debug "#{method}: #{url}"
|
214
|
-
begin
|
215
|
-
# first check if it does exits
|
216
|
-
RestClient::Request.execute({
|
217
|
-
:method => method,
|
218
|
-
:url => url,
|
219
|
-
:timeout => @options[:timeout],
|
220
|
-
:headers => {
|
221
|
-
:user_agent => GoodData.gem_version_string
|
222
|
-
}
|
223
|
-
}.merge(cookies))
|
224
|
-
rescue RestClient::Exception => e
|
225
|
-
if e.http_code == 404
|
226
|
-
method = :mkcol
|
227
|
-
GoodData.logger.debug "#{method}: #{url}"
|
228
|
-
RestClient::Request.execute({
|
229
|
-
:method => method,
|
230
|
-
:url => url,
|
231
|
-
:timeout => @options[:timeout],
|
232
|
-
:headers => {
|
233
|
-
:user_agent => GoodData.gem_version_string
|
234
|
-
}
|
235
|
-
}.merge(cookies))
|
236
|
-
end
|
237
|
-
end
|
238
|
-
end
|
239
|
-
|
240
|
-
payload = options[:stream] ? 'file' : File.read(file)
|
241
|
-
filename = options[:filename] || options[:stream] ? 'randome-filename.txt' : File.basename(file)
|
242
|
-
|
243
|
-
# Upload the file
|
244
|
-
# puts "uploading the file #{URI.join(url, filename).to_s}"
|
245
|
-
req = RestClient::Request.new(
|
246
|
-
:method => :put,
|
247
|
-
:url => URI.join(url, filename).to_s,
|
248
|
-
:timeout => @options[:timeout],
|
249
|
-
:headers => {
|
250
|
-
:user_agent => GoodData.gem_version_string
|
251
|
-
},
|
252
|
-
:payload => payload,
|
253
|
-
:raw_response => true,
|
254
|
-
:user => @username,
|
255
|
-
:password => @password
|
256
|
-
)
|
257
|
-
# .merge(cookies))
|
258
|
-
req.execute
|
259
|
-
true
|
260
|
-
end
|
261
|
-
|
262
|
-
def download(what, where, options = {})
|
263
|
-
staging_uri = options[:staging_url].to_s
|
264
|
-
url = staging_uri + what
|
265
|
-
req = RestClient::Request.new({
|
266
|
-
:method => 'GET',
|
267
|
-
:url => url,
|
268
|
-
:user => @username,
|
269
|
-
:password => @password
|
270
|
-
}.merge(cookies))
|
271
|
-
|
272
|
-
if where.is_a?(String)
|
273
|
-
File.open(where, 'w') do |f|
|
274
|
-
req.execute do |chunk, _, _|
|
275
|
-
f.write chunk
|
276
|
-
end
|
277
|
-
end
|
278
|
-
else
|
279
|
-
# Assume it is a IO stream
|
280
|
-
req.execute do |chunk, _, _|
|
281
|
-
where.write chunk
|
282
|
-
end
|
283
|
-
end
|
284
|
-
end
|
285
|
-
|
286
|
-
def connected?
|
287
|
-
@status == :logged_in
|
288
|
-
end
|
289
|
-
|
290
|
-
def disconnect
|
291
|
-
return if !connected? && !GoodData.connection.user['state']
|
292
|
-
GoodData.delete(GoodData.connection.user['state'])
|
293
|
-
@status = :not_connected
|
294
|
-
end
|
295
|
-
|
296
|
-
private
|
297
|
-
|
298
|
-
def create_server_connection(url, options)
|
299
|
-
RestClient::Resource.new url,
|
300
|
-
:timeout => options[:timeout],
|
301
|
-
:headers => {
|
302
|
-
:content_type => :json,
|
303
|
-
:accept => [:json, :zip],
|
304
|
-
:user_agent => GoodData.gem_version_string
|
305
|
-
}
|
306
|
-
end
|
307
|
-
|
308
|
-
def ensure_connection
|
309
|
-
connect if @status == :not_connected
|
310
|
-
end
|
311
|
-
|
312
|
-
def connect
|
313
|
-
GoodData.logger.info 'Connecting to GoodData...'
|
314
|
-
@status = :connecting
|
315
|
-
authenticate
|
316
|
-
end
|
317
|
-
|
318
|
-
def authenticate
|
319
|
-
credentials = {
|
320
|
-
'postUserLogin' => {
|
321
|
-
'login' => @username,
|
322
|
-
'password' => @password,
|
323
|
-
'remember' => 1
|
324
|
-
}
|
325
|
-
}
|
326
|
-
GoodData.logger.debug 'Logging in...'
|
327
|
-
@user = post(LOGIN_PATH, credentials, :dont_reauth => true)['userLogin']
|
328
|
-
refresh_token :dont_reauth => true # avoid infinite loop if refresh_token fails with 401
|
329
|
-
|
330
|
-
@status = :logged_in
|
331
|
-
end
|
332
|
-
|
333
|
-
def process_response(options = {}, &block)
|
334
|
-
begin
|
335
|
-
response = block.call
|
336
|
-
rescue RestClient::Unauthorized
|
337
|
-
raise $ERROR_INFO if options[:dont_reauth]
|
338
|
-
refresh_token
|
339
|
-
response = block.call
|
340
|
-
end
|
341
|
-
merge_cookies! response.cookies
|
342
|
-
content_type = response.headers[:content_type]
|
343
|
-
return response if options[:process] == false
|
344
|
-
|
345
|
-
if content_type == 'application/json' || content_type == 'application/json;charset=UTF-8'
|
346
|
-
result = response.to_str == '""' ? {} : MultiJson.load(response.to_str)
|
347
|
-
GoodData.logger.debug "Response: #{result.inspect}"
|
348
|
-
elsif content_type == 'application/zip'
|
349
|
-
result = response
|
350
|
-
GoodData.logger.debug 'Response: a zipped stream'
|
351
|
-
elsif response.headers[:content_length].to_s == '0'
|
352
|
-
result = nil
|
353
|
-
GoodData.logger.debug 'Response: Empty response possibly 204'
|
354
|
-
elsif response.code == 204
|
355
|
-
result = nil
|
356
|
-
GoodData.logger.debug 'Response: 204 no content'
|
357
|
-
else
|
358
|
-
fail "Unsupported response content type '%s':\n%s" % [content_type, response.to_str[0..127]]
|
359
|
-
end
|
360
|
-
result
|
361
|
-
rescue RestClient::Exception => e
|
362
|
-
GoodData.logger.debug "Response: #{e.response}"
|
363
|
-
raise $ERROR_INFO
|
364
|
-
end
|
365
|
-
|
366
|
-
def refresh_token(options = {})
|
367
|
-
GoodData.logger.debug 'Getting authentication token...'
|
368
|
-
begin
|
369
|
-
get TOKEN_PATH, :dont_reauth => true # avoid infinite loop GET fails with 401
|
370
|
-
rescue RestClient::Unauthorized
|
371
|
-
raise $ERROR_INFO if options[:dont_reauth]
|
372
|
-
authenticate
|
373
|
-
end
|
374
|
-
end
|
375
|
-
end
|
376
|
-
end
|