haveapi-client 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2450bb773a086b4732b000238a127e2b86bb3123
4
- data.tar.gz: 5749b665304e4a2d40f4104189404a31a666c000
3
+ metadata.gz: 1f73740e620eeb403f3cdec3491598f9b48bcfc4
4
+ data.tar.gz: 3e4d8544316b4a47f948f573de365055021725fc
5
5
  SHA512:
6
- metadata.gz: 1b2ce5925416ee00884e332ef48b75a72830b09df905c3d32afabf811913bde41db355baf23faeb4061f63dd2ce006cc869a43081c3493bef1c39867ba103155
7
- data.tar.gz: a703d0faed80a693d95906aae8d17030b7f135d41cbc16f9a0212d7c2db8c148968114d57ea1eeaa5e903497d6a20b68275ce8109da5b377eaa57521138751e4
6
+ metadata.gz: 037eee5721e264c732be091ee8fea13e0c96c63e824c8422b4a31e21b503f7e5be3e27114984e71dca37c7237f6c3a8f9392a7206bdd9e381757c0c42fac8d86
7
+ data.tar.gz: c891d03e167e84a0d7103498a65bfcee3146094de96124d998ea5a4d74d182001dc6609c6e0a58d6d39e327a52e04580cc36662287b8633d8e38345e200f88a0
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 Jakub Skokan
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md CHANGED
@@ -21,13 +21,15 @@ Or install it yourself as:
21
21
  ### CLI
22
22
  $ haveapi-cli -h
23
23
  Usage: haveapi-cli [options] <resource> <action> [objects ids] [-- [parameters]]
24
- -a, --api URL API URL
24
+ -u, --api URL API URL
25
+ -a, --auth METHOD Authentication method
25
26
  --list-versions List all available API versions
26
- --list-resources VERSION List all resource in API version
27
- --list-actions VERSION List all resources and actions in API version
27
+ --list-auth-methods [VERSION]
28
+ List available authentication methods
29
+ --list-resources [VERSION] List all resource in API version
30
+ --list-actions [VERSION] List all resources and actions in API version
28
31
  -r, --raw Print raw response as is
29
- -u, --username USER User name
30
- -p, --password PASSWORD Password
32
+ -s, --save Save credentials to config file for later use
31
33
  -v, --[no-]verbose Run verbosely
32
34
  -h, --help Show this message
33
35
 
@@ -35,20 +37,28 @@ Using the API example from
35
37
  [HaveAPI README](https://github.com/vpsfreecz/haveapi/blob/master/README.md#example),
36
38
  users would be listed with:
37
39
 
38
- $ haveapi-cli -a https://your.api.tld user index -u yourname -p yourpassword
40
+ $ haveapi-cli --url https://your.api.tld --auth basic --username yourname --password yourpassword user index
39
41
 
40
42
  Nested resources and object IDs:
41
43
 
42
- $ haveapi-cli -a https://your.api.tld user.invoice index 10 -u yourname -p yourpassword
44
+ $ haveapi-cli --url https://your.api.tld --auth basic --username yourname --password yourpassword user.invoice index 10
43
45
 
44
46
  where `10` is user ID.
47
+
48
+ User credentials can be saved to a config:
49
+
50
+ $ haveapi-cli --url https://your.api.tld --auth basic --username yourname --password yourpassword --save user index
51
+
52
+ When saved, they don't have to be specified as command line options:
53
+
54
+ $ haveapi-cli --url https://your.api.tld user index
45
55
 
46
56
  ### Client library
47
57
  ```ruby
48
58
  require 'haveapi/client'
49
59
 
50
60
  api = HaveAPI::Client::Client.new('https://your.api.tld')
51
- api.login('yourname', 'yourpassword')
61
+ api.authenticate(:basic, user: 'yourname', password: 'yourpassword')
52
62
 
53
63
  response = api.user.index
54
64
  p response.ok?
@@ -11,7 +11,7 @@ Gem::Specification.new do |spec|
11
11
  spec.summary =
12
12
  spec.description = 'Ruby API and CLI for HaveAPI'
13
13
  spec.homepage = ''
14
- spec.license = 'GPL'
14
+ spec.license = 'MIT'
15
15
 
16
16
  spec.files = `git ls-files -z`.split("\x0")
17
17
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
data/lib/haveapi/cli.rb CHANGED
@@ -1,2 +1,2 @@
1
- require 'haveapi/client'
2
- require 'haveapi/cli/cli'
1
+ require_relative 'client'
2
+ require_rel 'cli'
@@ -0,0 +1,52 @@
1
+ module HaveAPI::CLI
2
+ module Authentication
3
+ # Base class for CLI interface of authentication providers
4
+ class Base
5
+ class << self
6
+ # Register this class as authentication provider with +name+.
7
+ # The +name+ must be the same as is used in client auth provider
8
+ # and on server side.
9
+ # All providers have to register.
10
+ def register(name)
11
+ HaveAPI::CLI::Cli.register_auth_method(name, Kernel.const_get(to_s))
12
+ end
13
+ end
14
+
15
+ attr_accessor :communicator
16
+
17
+ def initialize(opts = {})
18
+ opts ||= {}
19
+
20
+ opts.each do |k, v|
21
+ instance_variable_set("@#{k}", v)
22
+ end
23
+ end
24
+
25
+ # Implement this method to add CLI options for auth provider.
26
+ # +opts+ is an instance of OptionParser.
27
+ # This method is NOT called if the auth provider has been loaded
28
+ # from the config and wasn't specified as a command line option
29
+ # and therefore all necessary information must be stored in the config.
30
+ def options(opts)
31
+
32
+ end
33
+
34
+ # Implement this method to check if all needed information
35
+ # for successful authentication are provided.
36
+ # Ask the user on stdin if something is missing.
37
+ def validate
38
+
39
+ end
40
+
41
+ # This method should call HaveAPI::Client::Communicator#authenticate
42
+ # with arguments specific for this authentication provider.
43
+ def authenticate
44
+
45
+ end
46
+
47
+ def save
48
+ @communicator.auth_save
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,28 @@
1
+ module HaveAPI::CLI::Authentication
2
+ class Basic < Base
3
+ register :basic
4
+
5
+ def options(opts)
6
+ opts.on('--username USER', 'User name') do |u|
7
+ @user = u
8
+ end
9
+
10
+ opts.on('--password PASSWORD', 'Password') do |p|
11
+ @password = p
12
+ end
13
+ end
14
+
15
+ def validate
16
+ @user ||= ask('User name: ') { |q| q.default = nil }.to_s
17
+
18
+ @password ||= ask('Password: ') do |q|
19
+ q.default = nil
20
+ q.echo = false
21
+ end.to_s
22
+ end
23
+
24
+ def authenticate
25
+ @communicator.authenticate(:basic, {user: @user, password: @password})
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,65 @@
1
+ module HaveAPI::CLI::Authentication
2
+ class Token < Base
3
+ register :token
4
+
5
+ def options(opts)
6
+ opts.on('--username USER', 'User name') do |u|
7
+ @user = u
8
+ end
9
+
10
+ opts.on('--password PASSWORD', 'Password') do |p|
11
+ @password = p
12
+ end
13
+
14
+ opts.on('--token TOKEN', 'Token') do |t|
15
+ @token = t
16
+ end
17
+
18
+ opts.on('--token-validity SECONDS', Integer,
19
+ 'How long will token be valid in seconds, 0 for forever') do |s|
20
+ @validity = s
21
+ end
22
+
23
+ opts.on('--new-token', 'Request new token') do
24
+ @token = nil
25
+ end
26
+
27
+ via = %i(query_param header)
28
+
29
+ opts.on('--token-via VIA', via,
30
+ 'Send token as a query parameter or in HTTP header',
31
+ "(#{via.join(', ')})") do |v|
32
+ @via = v.to_sym
33
+ end
34
+ end
35
+
36
+ def validate
37
+ return if @token
38
+
39
+ @user ||= ask('User name: ') { |q| q.default = nil }
40
+
41
+ @password ||= ask('Password: ') do |q|
42
+ q.default = nil
43
+ q.echo = false
44
+ end
45
+ end
46
+
47
+ def authenticate
48
+ @communicator.authenticate(:token, {
49
+ user: @user,
50
+ password: @password,
51
+ token: @token,
52
+ validity: @validity,
53
+ valid_to: @valid_to,
54
+ via: @via
55
+ })
56
+ end
57
+
58
+ def save
59
+ super.update({
60
+ via: @via,
61
+ validity: @validity
62
+ })
63
+ end
64
+ end
65
+ end
@@ -2,17 +2,30 @@ require 'optparse'
2
2
  require 'pp'
3
3
  require 'highline/import'
4
4
  require 'table_print'
5
+ require 'yaml'
5
6
 
6
7
  module HaveAPI
7
8
  module CLI
8
9
  class Cli
9
- def self.run
10
- c = new
10
+ class << self
11
+ attr_accessor :auth_methods
12
+
13
+ def run
14
+ c = new
15
+ end
16
+
17
+ def register_auth_method(name, klass)
18
+ @auth_methods ||= {}
19
+ @auth_methods[name] = klass
20
+ end
11
21
  end
12
22
 
13
23
  def initialize
24
+ @config = read_config || {}
14
25
  args, @opts = options
26
+
15
27
  @api = HaveAPI::Client::Communicator.new(api_url)
28
+ @api.identity = $0.split('/').last
16
29
 
17
30
  if @action
18
31
  method(@action.first).call( * @action[1..-1] )
@@ -37,6 +50,8 @@ module HaveAPI
37
50
 
38
51
  action = @api.get_action(resources, action, args[2..-1])
39
52
 
53
+ action.update_description(@api.describe_action(action)) if authenticate(action)
54
+
40
55
  @input_params = parameters(action)
41
56
 
42
57
  if action
@@ -78,14 +93,24 @@ module HaveAPI
78
93
  @global_opt = OptionParser.new do |opts|
79
94
  opts.banner = "Usage: #{$0} [options] <resource> <action> [objects ids] [-- [parameters]]"
80
95
 
81
- opts.on('-a', '--api URL', 'API URL') do |url|
96
+ opts.on('-u', '--api URL', 'API URL') do |url|
82
97
  options[:client] = url
83
98
  end
84
99
 
100
+ opts.on('-a', '--auth METHOD', Cli.auth_methods.keys, 'Authentication method') do |m|
101
+ options[:auth] = m
102
+ @auth = Cli.auth_methods[m].new(server_config(options[:client])[:auth][m])
103
+ @auth.options(opts)
104
+ end
105
+
85
106
  opts.on('--list-versions', 'List all available API versions') do
86
107
  @action = [:list_versions]
87
108
  end
88
109
 
110
+ opts.on('--list-auth-methods [VERSION]', 'List available authentication methods') do |v|
111
+ @action = [:list_auth, v && v.sub(/^v/, '')]
112
+ end
113
+
89
114
  opts.on('--list-resources [VERSION]', 'List all resource in API version') do |v|
90
115
  @action = [:list_resources, v && v.sub(/^v/, '')]
91
116
  end
@@ -98,12 +123,8 @@ module HaveAPI
98
123
  options[:raw] = true
99
124
  end
100
125
 
101
- opts.on('-u', '--username USER', 'User name') do |u|
102
- options[:user] = u
103
- end
104
-
105
- opts.on('-p', '--password PASSWORD', 'Password') do |p|
106
- options[:password] = p
126
+ opts.on('-s', '--save', 'Save credentials to config file for later use') do
127
+ options[:save] = true
107
128
  end
108
129
 
109
130
  opts.on('-v', '--[no-]verbose', 'Run verbosely') do |v|
@@ -127,8 +148,11 @@ module HaveAPI
127
148
 
128
149
  @global_opt.parse!(args)
129
150
 
130
- # p options
131
- #p ARGV
151
+ unless options[:auth]
152
+ cfg = server_config(options[:client])
153
+
154
+ @auth = Cli.auth_methods[cfg[:last_auth]].new(cfg[:auth][cfg[:last_auth]]) if cfg[:last_auth]
155
+ end
132
156
 
133
157
  [args, options]
134
158
  end
@@ -206,10 +230,18 @@ module HaveAPI
206
230
  end
207
231
  end
208
232
 
233
+ def list_auth(v=nil)
234
+ desc = @api.describe_api
235
+
236
+ desc[:versions][(v && v.to_sym) || desc[:default_version].to_s.to_sym][:authentication].each_key do |auth|
237
+ puts auth if Cli.auth_methods.has_key?(auth)
238
+ end
239
+ end
240
+
209
241
  def list_resources(v=nil)
210
242
  desc = @api.describe_api
211
243
 
212
- desc[:versions][v || desc[:default_version].to_s.to_sym][:resources].each do |resource, children|
244
+ desc[:versions][(v && v.to_sym) || desc[:default_version].to_s.to_sym][:resources].each do |resource, children|
213
245
  nested_resource(resource, children, false)
214
246
  end
215
247
  end
@@ -217,7 +249,7 @@ module HaveAPI
217
249
  def list_actions(v=nil)
218
250
  desc = @api.describe_api
219
251
 
220
- desc[:versions][v || desc[:default_version].to_s.to_sym][:resources].each do |resource, children|
252
+ desc[:versions][(v && v.to_sym) || desc[:default_version].to_s.to_sym][:resources].each do |resource, children|
221
253
  nested_resource(resource, children, true)
222
254
  end
223
255
  end
@@ -271,17 +303,31 @@ module HaveAPI
271
303
 
272
304
  return if response.empty?
273
305
 
274
- s = action.structure
275
306
  namespace = action.namespace(:output).to_sym
276
307
 
277
308
  case action.layout.to_sym
278
309
  when :list
279
- tp response[namespace]
310
+ cols = []
311
+
312
+ action.params.each do |name, p|
313
+ if p[:type] == 'Resource'
314
+ cols << {name => {display_method: ->(r) { r[name] && r[name][p[:value_label].to_sym] } }}
315
+ else
316
+ cols << name
317
+ end
318
+ end
319
+
320
+ tp response[namespace], *cols
280
321
 
281
322
 
282
323
  when :object
283
324
  response[namespace].each do |k, v|
284
- puts "#{k}: #{v}"
325
+
326
+ if action.params[k][:type] == 'Resource'
327
+ puts "#{k}: #{v[action.params[k][:value_label].to_sym]}"
328
+ else
329
+ puts "#{k}: #{v}"
330
+ end
285
331
  end
286
332
 
287
333
 
@@ -301,29 +347,66 @@ module HaveAPI
301
347
  end
302
348
  end
303
349
 
304
- def params_valid?(action)
350
+ def authenticate(action)
305
351
  if action.auth?
306
- @opts[:user] ||= ask('User name: ') { |q| q.default = nil }
352
+ if @auth
353
+ @auth.communicator = @api
354
+ @auth.validate
355
+ @auth.authenticate
356
+
357
+ if @opts[:save]
358
+ cfg = server_config(api_url)
359
+ cfg[:auth][@opts[:auth]] = @auth.save
360
+ cfg[:last_auth] = @opts[:auth]
361
+ write_config
362
+ end
307
363
 
308
- @opts[:password] ||= ask('Password: ') do |q|
309
- q.default = nil
310
- q.echo = false
364
+ else
365
+ # FIXME: exit as auth is needed and has not been selected
311
366
  end
312
- end
313
367
 
314
- if action.auth? && !(@opts[:user] || @opts[:password])
315
- return false
368
+ return true
316
369
  end
317
370
 
318
- @api.login(@opts[:user], @opts[:password])
371
+ false
372
+ end
319
373
 
320
- true
374
+ def params_valid?(action)
375
+ true # FIXME
321
376
  end
322
377
 
323
378
  protected
324
379
  def default_url
325
380
  'http://localhost:4567'
326
381
  end
382
+
383
+ def config_path
384
+ "#{Dir.home}/.haveapi-client.yml"
385
+ end
386
+
387
+ def write_config
388
+ File.open(config_path, 'w') do |f|
389
+ f.write(YAML.dump(@config))
390
+ end
391
+ end
392
+
393
+ def read_config
394
+ @config = YAML.load_file(config_path) if File.exists?(config_path)
395
+ end
396
+
397
+ def server_config(url)
398
+ unless @config[:servers]
399
+ @config[:servers] = [{url: url, auth: {}}]
400
+ return @config[:servers].first
401
+ end
402
+
403
+ @config[:servers].each do |s|
404
+ return s if s[:url] == url
405
+ end
406
+
407
+ @config[:servers] << {url: url, auth: {}}
408
+ @config[:servers].last
409
+ end
327
410
  end
328
411
  end
329
412
  end
@@ -12,6 +12,7 @@ module HaveAPI
12
12
  def execute(*args)
13
13
  ret = @api.call(self, *args)
14
14
  @prepared_url = nil
15
+ @prepared_help = nil
15
16
  ret
16
17
  end
17
18
 
@@ -51,11 +52,19 @@ module HaveAPI
51
52
  @spec[:url]
52
53
  end
53
54
 
55
+ def help
56
+ @spec[:help]
57
+ end
58
+
54
59
  # Url with resolved parameters.
55
60
  def prepared_url
56
61
  @prepared_url || @spec[:url]
57
62
  end
58
63
 
64
+ def prepared_help
65
+ @prepared_help || @spec[:help]
66
+ end
67
+
59
68
  def http_method
60
69
  @spec[:method]
61
70
  end
@@ -68,12 +77,18 @@ module HaveAPI
68
77
  apply_args(args)
69
78
  end
70
79
 
80
+ def update_description(spec)
81
+ @spec = spec
82
+ end
83
+
71
84
  private
72
85
  def apply_args(args)
73
86
  @prepared_url ||= @spec[:url].dup
87
+ @prepared_help ||= @spec[:help].dup
74
88
 
75
89
  args.each do |arg|
76
90
  @prepared_url.sub!(/:[a-zA-Z\-_]+/, arg.to_s)
91
+ @prepared_help.sub!(/:[a-zA-Z\-_]+/, arg.to_s)
77
92
  end
78
93
  end
79
94
  end
@@ -0,0 +1,79 @@
1
+ module HaveAPI::Client
2
+ module Authentication
3
+ # Raise this exception when authentication process fails somewhere
4
+ # outside action execution (in which access forbidden is raised from RestClient).
5
+ class AuthenticationFailed < Exception
6
+ def initialize(msg)
7
+ @msg = msg
8
+ end
9
+
10
+ def message
11
+ @msg
12
+ end
13
+ end
14
+
15
+ # Base class for all authentication providers.
16
+ #
17
+ # Authentication providers may reimplement only methods they need.
18
+ # You do not have to reimplement all methods.
19
+ class Base
20
+ class << self
21
+ # Register this class as authentication provider with +name+.
22
+ # The +name+ must be the same as is used in CLI auth provider (if any)
23
+ # and on server side.
24
+ # All providers have to register.
25
+ def register(name)
26
+ HaveAPI::Client::Communicator.register_auth_method(name, Kernel.const_get(to_s))
27
+ end
28
+ end
29
+
30
+ def initialize(communicator, description, opts)
31
+ @communicator = communicator
32
+ @desc = description
33
+ @opts = opts
34
+
35
+ setup
36
+ end
37
+
38
+ # Called right after initialize. Use this method to initialize provider.
39
+ def setup
40
+
41
+ end
42
+
43
+ # Return RestClient::Resource instance. This is mainly for HTTP basic auth.
44
+ def resource
45
+
46
+ end
47
+
48
+ # Called for each request. Returns a hash of query parameters.
49
+ def request_url_params
50
+ {}
51
+ end
52
+
53
+ # Called for each request. Returns a hash of parameters send in request
54
+ # body.
55
+ def request_payload
56
+ {}
57
+ end
58
+
59
+ # Called for each request. Returns a hash of HTTP headers.
60
+ def request_headers
61
+ {}
62
+ end
63
+
64
+ # Returns a hash of auth provider attributes to be saved e.g. in a file
65
+ # to be used later, without the user providing credentials again.
66
+ # You may wish to save a username or password (not recommended), tokens
67
+ # or whatever authentication provider needs to authenticate user
68
+ # without his input.
69
+ def save
70
+ @opts
71
+ end
72
+
73
+ # Load auth provider attributes from previous #save call.
74
+ def load(hash)
75
+ @opts = hash
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,9 @@
1
+ module HaveAPI::Client::Authentication
2
+ class Basic < Base
3
+ register :basic
4
+
5
+ def resource
6
+ RestClient::Resource.new(@communicator.url, @opts[:user], @opts[:password])
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,5 @@
1
+ module HaveAPI::Client::Authentication
2
+ class NoAuth < Base
3
+
4
+ end
5
+ end
@@ -0,0 +1,59 @@
1
+ module HaveAPI::Client::Authentication
2
+ class Token < Base
3
+ register :token
4
+
5
+ def setup
6
+ @via = @opts[:via] || :header
7
+ @token = @opts[:token]
8
+ @valid_to = @opts[:valid_to]
9
+
10
+ request_token unless @token
11
+
12
+ @configured = true
13
+ end
14
+
15
+ def request_url_params
16
+ return {} unless @configured
17
+ check_validity
18
+ @via == :query_param ? {@desc[:query_parameter] => @token} : {}
19
+ end
20
+
21
+ def request_headers
22
+ return {} unless @configured
23
+ check_validity
24
+ @via == :header ? {@desc[:http_header] => @token} : {}
25
+ end
26
+
27
+ def save
28
+ {token: @token, valid_to: @valid_to}
29
+ end
30
+
31
+ def load(hash)
32
+ @token = hash[:token]
33
+ @valid_to = hash[:valid_to]
34
+ end
35
+
36
+ protected
37
+ def request_token
38
+ a = HaveAPI::Client::Action.new(@communicator, :request, @desc[:resources][:token][:actions][:request], [])
39
+ ret = a.execute({login: @opts[:user], password: @opts[:password], validity: @opts[:validity] || 300})
40
+
41
+ raise AuthenticationFailed.new('bad username or password') unless ret[:status]
42
+
43
+ @token = ret[:response][:token][:token]
44
+
45
+ @valid_to = ret[:response][:token][:valid_to]
46
+ @valid_to = @valid_to && DateTime.iso8601(@valid_to).to_time
47
+ end
48
+
49
+ def check_validity
50
+ if @valid_to && @valid_to < Time.now
51
+ if @opts[:user] && @opts[:password]
52
+ request_token
53
+ else
54
+ raise AuthenticationFailed.new('token expired')
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -3,15 +3,17 @@ require 'pp'
3
3
  class HaveAPI::Client::Client
4
4
  attr_reader :resources
5
5
 
6
- def initialize(url, v=nil)
6
+ def initialize(url, v=nil, identity: 'haveapi-client')
7
7
  @version = v
8
- @api = VpsAdmin::API::Communicator.new(url)
8
+ @api = HaveAPI::Client::Communicator.new(url, v)
9
+ @api.identity = identity
9
10
 
10
11
  setup_api(@api.describe_api)
11
12
  end
12
13
 
13
- def login(*credentials)
14
- @api.login(*credentials)
14
+ # See Communicator#authenticate.
15
+ def authenticate(*args)
16
+ @api.authenticate(*args)
15
17
  end
16
18
 
17
19
  private
@@ -21,7 +23,7 @@ class HaveAPI::Client::Client
21
23
  @resources = {}
22
24
 
23
25
  description[:versions][v.to_s.to_sym][:resources].each do |name, desc|
24
- r = VpsAdmin::API::Resource.new(@api, name)
26
+ r = HaveAPI::Client::Resource.new(@api, name)
25
27
  r.setup(desc)
26
28
 
27
29
  define_singleton_method(name) do |*args|
@@ -7,14 +7,39 @@ require_rel '../../restclient_ext'
7
7
  module HaveAPI
8
8
  module Client
9
9
  class Communicator
10
- def initialize(url)
10
+ class << self
11
+ attr_reader :auth_methods
12
+
13
+ def register_auth_method(name, klass)
14
+ @auth_methods ||= {}
15
+ @auth_methods[name] = klass
16
+ end
17
+ end
18
+
19
+ attr_reader :url
20
+ attr_accessor :identity
21
+
22
+ def initialize(url, v = nil)
11
23
  @url = url
24
+ @auth = Authentication::NoAuth.new(self, {}, {})
12
25
  @rest = RestClient::Resource.new(@url)
13
- @version = 1
26
+ @version = v
27
+ @identity = 'haveapi-client-ruby'
14
28
  end
15
29
 
16
- def login(user, password)
17
- @rest = RestClient::Resource.new(@url, user, password)
30
+ # Authenticate user with selected +auth_method+.
31
+ # +auth_method+ is a name of registered authentication provider.
32
+ # +options+ are specific for each authentication provider.
33
+ def authenticate(auth_method, options = {})
34
+ desc = describe_api(@version)
35
+ desc = desc[:versions][desc[:default_version].to_s.to_sym] unless @version
36
+
37
+ @auth = self.class.auth_methods[auth_method].new(self, desc[:authentication][auth_method], options)
38
+ @rest = @auth.resource || @rest
39
+ end
40
+
41
+ def auth_save
42
+ @auth.save
18
43
  end
19
44
 
20
45
  def describe_api(v=nil)
@@ -23,7 +48,7 @@ module HaveAPI
23
48
 
24
49
  def describe_resource(path)
25
50
  api = describe_api
26
- tmp = describe_api[:versions][ describe_api[:default_version].to_s.to_sym ]
51
+ tmp = api[:versions][ api[:default_version].to_s.to_sym ]
27
52
 
28
53
  path.each do |r|
29
54
  tmp = tmp[:resources][r.to_sym]
@@ -34,12 +59,13 @@ module HaveAPI
34
59
  tmp
35
60
  end
36
61
 
37
- def describe_action(v, r)
38
-
62
+ def describe_action(action)
63
+ description_for(action.prepared_help)
39
64
  end
40
65
 
41
66
  def get_action(resources, action, args)
42
67
  @spec ||= describe_api(@version)
68
+ @spec = @spec[:versions][@spec[:default_version].to_s.to_sym] unless @version
43
69
 
44
70
  tmp = @spec
45
71
 
@@ -63,8 +89,8 @@ module HaveAPI
63
89
  input_namespace = action.namespace(:input)
64
90
 
65
91
  if %w(POST PUT).include?(action.http_method)
66
- args << {input_namespace => params}.to_json
67
- args << {:content_type => :json, :accept => :json}
92
+ args << {input_namespace => params}.update(@auth.request_payload).to_json
93
+ args << {content_type: :json, accept: :json, user_agent: @identity}.update(@auth.request_headers)
68
94
 
69
95
  elsif %w(GET DELETE).include?(action.http_method)
70
96
  get_params = {}
@@ -73,7 +99,7 @@ module HaveAPI
73
99
  get_params["#{input_namespace}[#{k}]"] = v
74
100
  end
75
101
 
76
- args << {params: get_params, accept: :json}
102
+ args << {params: get_params.update(@auth.request_url_params), accept: :json, user_agent: @identity}.update(@auth.request_headers)
77
103
  end
78
104
 
79
105
  begin
@@ -117,7 +143,10 @@ module HaveAPI
117
143
  end
118
144
 
119
145
  def description_for(path)
120
- parse(@rest[path].get_options)
146
+ parse(@rest[path].get_options({
147
+ params: @auth.request_payload.update(@auth.request_url_params),
148
+ user_agent: @identity
149
+ }.update(@auth.request_headers)))
121
150
  end
122
151
 
123
152
  def parse(str)
@@ -1,5 +1,5 @@
1
1
  module HaveAPI
2
2
  module Client
3
- VERSION = '0.1.1'
3
+ VERSION = '0.2.0'
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: haveapi-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jakub Skokan
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-05-19 00:00:00.000000000 Z
11
+ date: 2014-08-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -131,14 +131,22 @@ extensions: []
131
131
  extra_rdoc_files: []
132
132
  files:
133
133
  - Gemfile
134
+ - LICENSE.txt
134
135
  - README.md
135
136
  - Rakefile
136
137
  - bin/haveapi-cli
137
138
  - haveapi-client.gemspec
138
139
  - lib/haveapi/cli.rb
140
+ - lib/haveapi/cli/authentication/base.rb
141
+ - lib/haveapi/cli/authentication/basic.rb
142
+ - lib/haveapi/cli/authentication/token.rb
139
143
  - lib/haveapi/cli/cli.rb
140
144
  - lib/haveapi/client.rb
141
145
  - lib/haveapi/client/action.rb
146
+ - lib/haveapi/client/authentication/base.rb
147
+ - lib/haveapi/client/authentication/basic.rb
148
+ - lib/haveapi/client/authentication/noauth.rb
149
+ - lib/haveapi/client/authentication/token.rb
142
150
  - lib/haveapi/client/client.rb
143
151
  - lib/haveapi/client/communicator.rb
144
152
  - lib/haveapi/client/exceptions.rb
@@ -150,7 +158,7 @@ files:
150
158
  - lib/restclient_ext/resource.rb
151
159
  homepage: ''
152
160
  licenses:
153
- - GPL
161
+ - MIT
154
162
  metadata: {}
155
163
  post_install_message:
156
164
  rdoc_options: []