Class: GoodData::Rest::Client

Inherits:
Object
  • Object
show all
Includes:
Mixin::Inspector
Defined in:
lib/gooddata/rest/client.rb

Overview

User's interface to GoodData Platform.

MUST provide way to use - DELETE, GET, POST, PUT SHOULD provide way to use - HEAD, Bulk GET ...

Constant Summary collapse

DEFAULT_CONNECTION_IMPLEMENTATION =

Constants

GoodData::Rest::Connection
DEFAULT_SLEEP_INTERVAL =
10
DEFAULT_POLL_TIME_LIMIT =

5 hours

5 * 60 * 60
@@instance =

Class variables

nil

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Mixin::Inspector

#inspect, inspected

Constructor Details

#initialize(opts) ⇒ Client

Constructor of client

Parameters:

  • opts (Hash)

    Client options

Options Hash (opts):

  • :username (String)

    Username used for authentication

  • :password (String)

    Password used for authentication

  • :connection_factory (Object)

    Object able to create new instances of GoodData::Rest::Connection

  • :connection (GoodData::Rest::Connection)

    Existing GoodData::Rest::Connection



149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
# File 'lib/gooddata/rest/client.rb', line 149

def initialize(opts)
  # TODO: Decide if we want to pass the options directly or not
  @opts = opts

  @connection_factory = @opts[:connection_factory] || DEFAULT_CONNECTION_IMPLEMENTATION

  # TODO: See previous TODO
  # Create connection
  @connection = opts[:connection] || @connection_factory.new(opts)

  # Connect
  connect

  # Create factory bound to previously created connection
  @factory = ObjectFactory.new(self)
end

Instance Attribute Details

#connectionObject (readonly)

Decide if we need provide direct access to connection



41
42
43
# File 'lib/gooddata/rest/client.rb', line 41

def connection
  @connection
end

#factoryObject (readonly)

TODO: Decide if we need provide direct access to factory



44
45
46
# File 'lib/gooddata/rest/client.rb', line 44

def factory
  @factory
end

#optsObject (readonly)

Returns the value of attribute opts.



45
46
47
# File 'lib/gooddata/rest/client.rb', line 45

def opts
  @opts
end

Class Method Details

.connect(username, password = 'aaaa', opts = {}) ⇒ GoodData::Rest::Client

Globally available way to connect (and create client and set global instance)

HACK

To make transition from old implementation to new one following HACK IS TEMPORARILY ENGAGED!

  1. First call of #connect sets the GoodData::Rest::Client.instance (static, singleton instance)
  2. There are METHOD functions with same signature as their CLASS counterparts using singleton instance

Example

client = GoodData.connect('[email protected]', 's3cr3tp4sw0rd')

Parameters:

  • username (String)

    Username to be used for authentication

  • password (String) (defaults to: 'aaaa')

    Password to be used for authentication

Returns:



69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/gooddata/rest/client.rb', line 69

def connect(username, password = 'aaaa', opts = {})
  if username.nil? && password.nil?
    username = ENV['GD_GEM_USER']
    password = ENV['GD_GEM_PASSWORD']
  end

  username = GoodData::Helpers.symbolize_keys(username) if username.is_a?(Hash)

  new_opts = opts.dup
  if username.is_a?(Hash) && username.key?(:sst_token)
    new_opts = new_opts.merge(username)
  elsif username.is_a? Hash
    new_opts = new_opts.merge(username)
    new_opts[:username] = username[:login] || username[:user] || username[:username]
    new_opts[:password] = username[:password]
  elsif username.nil? && password.nil? && opts.blank?
    new_opts = Helpers::AuthHelper.read_credentials
  else
    new_opts[:username] = username
    new_opts[:password] = password
  end

  new_opts = { verify_ssl: true }.merge(new_opts)
  if username.is_a?(Hash) && username[:cookies]
    new_opts[:sst_token] = username[:cookies]['GDCAuthSST']
    new_opts[:cookies] = username[:cookies]
  end

  unless new_opts[:sst_token]
    fail ArgumentError, 'No username specified' if new_opts[:username].nil?
    fail ArgumentError, 'No password specified' if new_opts[:password].nil?
  end

  if username.is_a?(Hash) && username.key?(:server)
    new_opts[:server] = username[:server]
  end

  client = Client.new(new_opts)
  GoodData.logger.info("Connected to server with webdav path #{client.user_webdav_path}")

  if client
    at_exit do
      puts client.connection.stats_table if client && client.connection && (GoodData.stats_on? || client.stats_on?)
    end
  end

  # HACK: This line assigns class instance # if not done yet
  @@instance = client # rubocop:disable ClassVars
end

.connect_sso(sso) ⇒ Object



119
120
121
122
# File 'lib/gooddata/rest/client.rb', line 119

def connect_sso(sso)
  client = Client.new(sso)
  client
end

.connectionObject Also known as: client



131
132
133
# File 'lib/gooddata/rest/client.rb', line 131

def connection
  @@instance
end

.disconnectObject



124
125
126
127
128
129
# File 'lib/gooddata/rest/client.rb', line 124

def disconnect
  if @@instance # rubocop:disable Style/GuardClause
    @@instance.disconnect
    @@instance = nil # rubocop:disable ClassVars
  end
end

.retryable(options = {}, &block) ⇒ Object

Retry block if exception thrown



136
137
138
# File 'lib/gooddata/rest/client.rb', line 136

def retryable(options = {}, &block)
  GoodData::Rest::Connection.retryable(options, &block)
end

Instance Method Details

#connectObject



192
193
194
195
196
197
# File 'lib/gooddata/rest/client.rb', line 192

def connect
  username = @opts[:username]
  password = @opts[:password]

  @connection.connect(username, password, @opts)
end

#create(klass, data = {}, opts = {}) ⇒ Object

Factory stuff



214
215
216
# File 'lib/gooddata/rest/client.rb', line 214

def create(klass, data = {}, opts = {})
  @factory.create(klass, data, opts)
end

#create_datawarehouse(opts = {}) ⇒ Object



207
208
209
# File 'lib/gooddata/rest/client.rb', line 207

def create_datawarehouse(opts = {})
  GoodData::DataWarehouse.create({ client: self }.merge(opts))
end

#create_project(options = { title: 'Project' }) ⇒ Object



166
167
168
# File 'lib/gooddata/rest/client.rb', line 166

def create_project(options = { title: 'Project' })
  GoodData::Project.create({ client: self }.merge(options))
end

#create_project_from_blueprint(blueprint, options = {}) ⇒ Object



170
171
172
# File 'lib/gooddata/rest/client.rb', line 170

def create_project_from_blueprint(blueprint, options = {})
  GoodData::Model::ProjectCreator.migrate(options.merge(spec: blueprint, client: self))
end

#delete(uri, opts = {}) ⇒ Object

Rest

HTTP DELETE

Parameters:



258
259
260
# File 'lib/gooddata/rest/client.rb', line 258

def delete(uri, opts = {})
  @connection.delete uri, opts
end

#disconnectObject



199
200
201
# File 'lib/gooddata/rest/client.rb', line 199

def disconnect
  @connection.disconnect
end

#domain(domain_name) ⇒ Object



174
175
176
# File 'lib/gooddata/rest/client.rb', line 174

def domain(domain_name)
  GoodData::Domain[domain_name, :client => self]
end

#download(source_relative_path, target_file_path, options = {}) ⇒ Object

Downloads file from staging

Parameters:

  • source_relative_path (String)

    path relative to @param options[:staging_url]

  • target_file_path (String)

    path to be downloaded to

  • options (Hash) (defaults to: {})

    must contain :staging_url key (file will be downloaded from :staging_url + source_relative_path)



375
376
377
# File 'lib/gooddata/rest/client.rb', line 375

def download(source_relative_path, target_file_path, options = {})
  @connection.download source_relative_path, target_file_path, options
end

#download_from_user_webdav(source_relative_path, target_file_path, options = { client: GoodData.client }) ⇒ Object



379
380
381
382
# File 'lib/gooddata/rest/client.rb', line 379

def download_from_user_webdav(source_relative_path, target_file_path, options = { client: GoodData.client })
  download(source_relative_path, target_file_path, options.merge(:directory => options[:directory],
                                                                 :staging_url => user_webdav_path))
end

#find(klass, opts = {}) ⇒ Object



218
219
220
# File 'lib/gooddata/rest/client.rb', line 218

def find(klass, opts = {})
  @factory.find(klass, opts)
end

#generate_request_idObject



248
249
250
# File 'lib/gooddata/rest/client.rb', line 248

def generate_request_id
  @connection.generate_request_id
end

#get(uri, opts = {}, &block) ⇒ Object

HTTP GET

Parameters:



265
266
267
# File 'lib/gooddata/rest/client.rb', line 265

def get(uri, opts = {}, & block)
  @connection.get uri, opts, & block
end


393
394
395
# File 'lib/gooddata/rest/client.rb', line 393

def links
  GoodData::Helpers.get_path(get('/gdc'), %w(about links))
end

#poll_on_code(link, options = {}) ⇒ Hash

Generalizaton of poller. Since we have quite a variation of how async proceses are handled this is a helper that should help you with resources where the information about "Are we done" is the http code of response. By default we repeat as long as the code == 202. You can change the code if necessary. It expects the URI as an input where it can poll. It returns the value of last poll. In majority of cases these are the data that you need.

Parameters:

  • link (String)

    Link for polling

  • options (Hash) (defaults to: {})

    Options

Returns:

  • (Hash)

    Result of polling



302
303
304
305
306
307
308
309
310
311
312
313
314
315
# File 'lib/gooddata/rest/client.rb', line 302

def poll_on_code(link, options = {})
  code = options[:code] || 202
  process = options[:process]

  response = poll_on_response(link, options.merge(:process => false)) do |resp|
    resp.code == code
  end

  if process == false
    response
  else
    get(link)
  end
end

#poll_on_response(link, options = {}, &bl) ⇒ Hash

Generalizaton of poller. Since we have quite a variation of how async proceses are handled this is a helper that should help you with resources where the information about "Are we done" is inside the response. It expects the URI as an input where it can poll and a block that should return either true -> 'meaning we are done' or false -> meaning sleep and repeat. It returns the value of last poll. In majority of cases these are the data that you need

Parameters:

  • link (String)

    Link for polling

  • options (Hash) (defaults to: {})

    Options

Returns:

  • (Hash)

    Result of polling



326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
# File 'lib/gooddata/rest/client.rb', line 326

def poll_on_response(link, options = {}, &bl)
  sleep_interval = options[:sleep_interval] || DEFAULT_SLEEP_INTERVAL
  time_limit = options[:time_limit] || DEFAULT_POLL_TIME_LIMIT
  process = options[:process] == false ? false : true

  # get the first status and start the timer
  response = get(link, process: process)
  poll_start = Time.now

  while bl.call(response)
    limit_breached = time_limit && (Time.now - poll_start > time_limit)
    if limit_breached
      fail ExecutionLimitExceeded, "The time limit #{time_limit} secs for polling on #{link} is over"
    end
    sleep sleep_interval
    GoodData::Rest::Client.retryable(:tries => Helpers::GD_MAX_RETRY, :refresh_token => proc { connection.refresh_token }) do
      response = get(link, process: process)
    end
  end
  response
end

#post(uri, data, opts = {}) ⇒ Object

HTTP POST

Parameters:



358
359
360
# File 'lib/gooddata/rest/client.rb', line 358

def post(uri, data, opts = {})
  @connection.post uri, data, opts
end

#processes(id = :all) ⇒ Object



188
189
190
# File 'lib/gooddata/rest/client.rb', line 188

def processes(id = :all)
  GoodData::Process[id, client: self]
end

#project_is_accessible?(id) ⇒ Boolean

Returns:

  • (Boolean)


178
179
180
181
182
# File 'lib/gooddata/rest/client.rb', line 178

def project_is_accessible?(id)
  projects(id) && true
rescue
  true
end

#project_webdav_path(opts = { project: GoodData.project }) ⇒ Object



269
270
271
272
273
274
275
276
277
278
279
280
281
# File 'lib/gooddata/rest/client.rb', line 269

def project_webdav_path(opts = { project: GoodData.project })
  p = opts[:project]
  fail ArgumentError, 'No :project specified' if p.nil?

  project = GoodData::Project[p, opts]
  fail ArgumentError, 'Wrong :project specified' if project.nil?

  url = project.links['uploads']
  fail 'Project WebDAV not supported in this Data Center' unless url

  GoodData.logger.warn 'Beware! Project webdav is deprecated and should not be used.'
  url
end

#projects(id = :all) ⇒ Object



184
185
186
# File 'lib/gooddata/rest/client.rb', line 184

def projects(id = :all)
  GoodData::Project[id, client: self]
end

#put(uri, data, opts = {}) ⇒ Object

HTTP PUT

Parameters:



351
352
353
# File 'lib/gooddata/rest/client.rb', line 351

def put(uri, data, opts = {})
  @connection.put uri, data, opts
end

#resource(res_name) ⇒ Object

Gets resource by name



223
224
225
226
# File 'lib/gooddata/rest/client.rb', line 223

def resource(res_name)
  puts "Getting resource '#{res_name}'"
  nil
end

#stats_offObject



236
237
238
# File 'lib/gooddata/rest/client.rb', line 236

def stats_off
  @stats = false
end

#stats_onObject



240
241
242
# File 'lib/gooddata/rest/client.rb', line 240

def stats_on
  @stats = true
end

#stats_on?Boolean

Returns:

  • (Boolean)


244
245
246
# File 'lib/gooddata/rest/client.rb', line 244

def stats_on?
  @stats
end

#upload(file, options = {}) ⇒ Object

Uploads file to staging

Parameters:

  • file (String)

    file to be uploaded

  • options (Hash) (defaults to: {})

    must contain :staging_url key (file will be uploaded to :staging_url + File.basename(file))



366
367
368
# File 'lib/gooddata/rest/client.rb', line 366

def upload(file, options = {})
  @connection.upload file, options
end

#upload_to_user_webdav(file, options = {}) ⇒ Object



384
385
386
387
# File 'lib/gooddata/rest/client.rb', line 384

def upload_to_user_webdav(file, options = {})
  upload(file, options.merge(:directory => options[:directory],
                             :staging_url => user_webdav_path))
end

#user(id = nil) ⇒ Object



228
229
230
231
232
233
234
# File 'lib/gooddata/rest/client.rb', line 228

def user(id = nil)
  if id
    create(GoodData::Profile, get(id))
  else
    create(GoodData::Profile, @connection.user)
  end
end

#user_webdav_pathObject



283
284
285
286
287
288
289
290
291
# File 'lib/gooddata/rest/client.rb', line 283

def user_webdav_path
  uri = if opts[:webdav_server]
          opts[:webdav_server]
        else
          links.find { |i| i['category'] == 'uploads' }['link']
        end
  res = uri.chomp('/') + '/'
  res[0] == '/' ? "#{connection.server}#{res}" : res
end

#warehouses(id = :all) ⇒ Object



203
204
205
# File 'lib/gooddata/rest/client.rb', line 203

def warehouses(id = :all)
  GoodData::DataWarehouse[id, client: self]
end

#with_project(pid, &block) ⇒ Object



389
390
391
# File 'lib/gooddata/rest/client.rb', line 389

def with_project(pid, &block)
  GoodData.with_project(pid, client: self, &block)
end