haveapi-client 0.20.0 → 0.21.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +5 -0
- data/Rakefile +0 -1
- data/haveapi-client.gemspec +7 -10
- data/lib/haveapi/cli/action_state.rb +50 -51
- data/lib/haveapi/cli/authentication/base.rb +4 -9
- data/lib/haveapi/cli/authentication/basic.rb +3 -1
- data/lib/haveapi/cli/authentication/token.rb +7 -8
- data/lib/haveapi/cli/cli.rb +115 -127
- data/lib/haveapi/cli/command.rb +2 -4
- data/lib/haveapi/cli/commands/action_state_wait.rb +9 -9
- data/lib/haveapi/cli/example_formatter.rb +8 -8
- data/lib/haveapi/cli/output_formatter.rb +39 -36
- data/lib/haveapi/cli/utils.rb +5 -5
- data/lib/haveapi/cli.rb +1 -1
- data/lib/haveapi/client/action.rb +17 -20
- data/lib/haveapi/client/action_state.rb +3 -7
- data/lib/haveapi/client/authentication/base.rb +4 -7
- data/lib/haveapi/client/authentication/basic.rb +2 -2
- data/lib/haveapi/client/authentication/noauth.rb +0 -1
- data/lib/haveapi/client/authentication/token.rb +28 -27
- data/lib/haveapi/client/client.rb +12 -6
- data/lib/haveapi/client/communicator.rb +33 -35
- data/lib/haveapi/client/exceptions.rb +5 -9
- data/lib/haveapi/client/parameters/resource.rb +1 -0
- data/lib/haveapi/client/parameters/typed.rb +3 -5
- data/lib/haveapi/client/params.rb +8 -8
- data/lib/haveapi/client/resource.rb +12 -13
- data/lib/haveapi/client/resource_instance.rb +71 -72
- data/lib/haveapi/client/resource_instance_list.rb +2 -1
- data/lib/haveapi/client/response.rb +8 -8
- data/lib/haveapi/client/validator.rb +7 -8
- data/lib/haveapi/client/validators/confirmation.rb +1 -0
- data/lib/haveapi/client/validators/length.rb +1 -0
- data/lib/haveapi/client/validators/numericality.rb +2 -1
- data/lib/haveapi/client/validators/presence.rb +1 -0
- data/lib/haveapi/client/version.rb +2 -2
- data/lib/haveapi/client.rb +2 -2
- data/lib/restclient_ext/resource.rb +5 -5
- data/shell.nix +1 -1
- metadata +15 -43
@@ -1,12 +1,12 @@
|
|
1
1
|
module HaveAPI::CLI
|
2
2
|
class OutputFormatter
|
3
|
-
def self.
|
4
|
-
f = new(*
|
5
|
-
f.
|
3
|
+
def self.to_s(*)
|
4
|
+
f = new(*)
|
5
|
+
f.to_s
|
6
6
|
end
|
7
7
|
|
8
|
-
def self.print(
|
9
|
-
f = new(
|
8
|
+
def self.print(*, **)
|
9
|
+
f = new(*, **)
|
10
10
|
f.print
|
11
11
|
end
|
12
12
|
|
@@ -18,31 +18,29 @@ module HaveAPI::CLI
|
|
18
18
|
@empty = empty
|
19
19
|
|
20
20
|
if @layout.nil?
|
21
|
-
if many?
|
22
|
-
|
21
|
+
@layout = if many?
|
22
|
+
:columns
|
23
23
|
|
24
|
-
|
25
|
-
|
26
|
-
|
24
|
+
else
|
25
|
+
:rows
|
26
|
+
end
|
27
27
|
end
|
28
28
|
|
29
29
|
if cols
|
30
30
|
@cols = parse_cols(cols)
|
31
31
|
|
32
|
-
|
33
|
-
|
34
|
-
@cols ||= parse_cols(@objects.first.keys)
|
32
|
+
elsif @objects.is_a?(::Array)
|
33
|
+
@cols ||= parse_cols(@objects.first.keys) # A list of items
|
35
34
|
|
36
|
-
|
37
|
-
|
35
|
+
elsif @objects.is_a?(::Hash) # Single item
|
36
|
+
@cols ||= parse_cols(@objects.keys)
|
38
37
|
|
39
|
-
|
40
|
-
|
41
|
-
end
|
38
|
+
else
|
39
|
+
raise "unsupported type #{@objects.class}"
|
42
40
|
end
|
43
41
|
end
|
44
42
|
|
45
|
-
def
|
43
|
+
def to_s
|
46
44
|
@out = ''
|
47
45
|
generate
|
48
46
|
@out
|
@@ -54,6 +52,7 @@ module HaveAPI::CLI
|
|
54
52
|
end
|
55
53
|
|
56
54
|
protected
|
55
|
+
|
57
56
|
def parse_cols(cols)
|
58
57
|
ret = []
|
59
58
|
|
@@ -65,7 +64,7 @@ module HaveAPI::CLI
|
|
65
64
|
if c.is_a?(::String) || c.is_a?(::Symbol)
|
66
65
|
base.update({
|
67
66
|
name: c,
|
68
|
-
label: c.to_s.upcase
|
67
|
+
label: c.to_s.upcase
|
69
68
|
})
|
70
69
|
ret << base
|
71
70
|
|
@@ -74,7 +73,7 @@ module HaveAPI::CLI
|
|
74
73
|
ret << base
|
75
74
|
|
76
75
|
else
|
77
|
-
|
76
|
+
raise "unsupported column type #{c.class}"
|
78
77
|
end
|
79
78
|
end
|
80
79
|
|
@@ -83,6 +82,7 @@ module HaveAPI::CLI
|
|
83
82
|
|
84
83
|
def generate
|
85
84
|
return if @cols.empty?
|
85
|
+
|
86
86
|
prepare
|
87
87
|
|
88
88
|
case @layout
|
@@ -93,7 +93,7 @@ module HaveAPI::CLI
|
|
93
93
|
rows
|
94
94
|
|
95
95
|
else
|
96
|
-
|
96
|
+
raise "unsupported layout '#{@layout}'"
|
97
97
|
end
|
98
98
|
end
|
99
99
|
|
@@ -103,21 +103,21 @@ module HaveAPI::CLI
|
|
103
103
|
|
104
104
|
formatters = @cols.map do |c|
|
105
105
|
ret = case c[:align].to_sym
|
106
|
-
|
107
|
-
|
106
|
+
when :right
|
107
|
+
"%#{col_width(i, c)}s"
|
108
108
|
|
109
|
-
|
110
|
-
|
111
|
-
|
109
|
+
else
|
110
|
+
"%-#{col_width(i, c)}s"
|
111
|
+
end
|
112
112
|
|
113
113
|
i += 1
|
114
114
|
ret
|
115
115
|
end.join(' ')
|
116
116
|
|
117
|
-
line
|
117
|
+
line format(formatters, * @cols.map { |c| c[:label] }) if @header
|
118
118
|
|
119
119
|
@str_objects.each do |o|
|
120
|
-
line
|
120
|
+
line format(formatters, *o)
|
121
121
|
end
|
122
122
|
end
|
123
123
|
|
@@ -131,13 +131,15 @@ module HaveAPI::CLI
|
|
131
131
|
|
132
132
|
if o[i].is_a?(::String) && o[i].index("\n")
|
133
133
|
lines = o[i].split("\n")
|
134
|
-
v = ([lines.first] + lines[1
|
134
|
+
v = ([lines.first] + lines[1..].map { |l| (' ' * (w + 3)) + l }).join("\n")
|
135
135
|
|
136
136
|
else
|
137
137
|
v = o[i]
|
138
138
|
end
|
139
139
|
|
140
|
-
|
140
|
+
# rubocop:disable Lint/FormatParameterMismatch
|
141
|
+
line format("%#{w}s: %s", c[:label], v)
|
142
|
+
# rubocop:enable Lint/FormatParameterMismatch
|
141
143
|
end
|
142
144
|
|
143
145
|
line
|
@@ -146,7 +148,7 @@ module HaveAPI::CLI
|
|
146
148
|
|
147
149
|
def line(str = '')
|
148
150
|
if @out
|
149
|
-
@out += str
|
151
|
+
@out += "#{str}\n"
|
150
152
|
|
151
153
|
else
|
152
154
|
puts str
|
@@ -160,7 +162,7 @@ module HaveAPI::CLI
|
|
160
162
|
arr = []
|
161
163
|
|
162
164
|
@cols.each do |c|
|
163
|
-
v = o[
|
165
|
+
v = o[c[:name]]
|
164
166
|
str = (c[:display] ? c[:display].call(v) : v)
|
165
167
|
str = @empty if !str || (str.is_a?(::String) && str.empty?)
|
166
168
|
|
@@ -172,7 +174,7 @@ module HaveAPI::CLI
|
|
172
174
|
|
173
175
|
if @sort
|
174
176
|
col_i = @cols.index { |c| c[:name] == @sort }
|
175
|
-
|
177
|
+
raise "unknown column '#{@sort}'" unless col_i
|
176
178
|
|
177
179
|
@str_objects.sort! do |a, b|
|
178
180
|
a_i = a[col_i]
|
@@ -181,6 +183,7 @@ module HaveAPI::CLI
|
|
181
183
|
next 0 if a_i == @empty && b_i == @empty
|
182
184
|
next -1 if a_i == @empty && b_i != @empty
|
183
185
|
next 1 if a_i != @empty && b_i == @empty
|
186
|
+
|
184
187
|
a_i <=> b_i
|
185
188
|
end
|
186
189
|
end
|
@@ -211,9 +214,9 @@ module HaveAPI::CLI
|
|
211
214
|
w + 1
|
212
215
|
end
|
213
216
|
|
214
|
-
def each_object
|
217
|
+
def each_object(&)
|
215
218
|
if @objects.is_a?(::Array)
|
216
|
-
@objects.each
|
219
|
+
@objects.each(&)
|
217
220
|
|
218
221
|
else
|
219
222
|
yield(@objects)
|
data/lib/haveapi/cli/utils.rb
CHANGED
@@ -6,12 +6,12 @@ module HaveAPI::CLI
|
|
6
6
|
ret = '--'
|
7
7
|
name = name.to_s.dasherize
|
8
8
|
|
9
|
-
if p[:type] == 'Boolean'
|
10
|
-
|
9
|
+
ret += if p[:type] == 'Boolean'
|
10
|
+
"[no-]#{name}"
|
11
11
|
|
12
|
-
|
13
|
-
|
14
|
-
|
12
|
+
else
|
13
|
+
"#{name} [#{name.underscore.upcase}]"
|
14
|
+
end
|
15
15
|
|
16
16
|
ret
|
17
17
|
end
|
data/lib/haveapi/cli.rb
CHANGED
@@ -29,15 +29,11 @@ module HaveAPI::Client
|
|
29
29
|
params_arg = params.to_api
|
30
30
|
end
|
31
31
|
|
32
|
-
ret = @api.call(self, params_arg, raw:
|
32
|
+
ret = @api.call(self, params_arg, raw:)
|
33
33
|
reset
|
34
34
|
ret
|
35
35
|
end
|
36
36
|
|
37
|
-
def name
|
38
|
-
@name
|
39
|
-
end
|
40
|
-
|
41
37
|
def auth?
|
42
38
|
@spec[:auth]
|
43
39
|
end
|
@@ -167,12 +163,12 @@ module HaveAPI::Client
|
|
167
163
|
|
168
164
|
loop do
|
169
165
|
res = client.action_state.poll(
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
166
|
+
id,
|
167
|
+
timeout: interval,
|
168
|
+
update_in:,
|
169
|
+
status: last[:status],
|
170
|
+
current: last[:current],
|
171
|
+
total: last[:total]
|
176
172
|
)
|
177
173
|
|
178
174
|
state = ActionState.new(res)
|
@@ -193,18 +189,19 @@ module HaveAPI::Client
|
|
193
189
|
if ret.is_a?(Response)
|
194
190
|
# The cancel is not a blocking operation, return immediately
|
195
191
|
raise ActionFailed, ret unless ret.ok?
|
192
|
+
|
196
193
|
return ret
|
197
194
|
end
|
198
195
|
|
199
196
|
# Cancel is a blocking operation
|
200
197
|
if cancel_block
|
201
198
|
return wait_for_completion(
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
199
|
+
client,
|
200
|
+
ret,
|
201
|
+
interval:,
|
202
|
+
timeout:,
|
203
|
+
update_in:,
|
204
|
+
&cancel_block
|
208
205
|
)
|
209
206
|
end
|
210
207
|
|
@@ -215,16 +212,15 @@ module HaveAPI::Client
|
|
215
212
|
end
|
216
213
|
|
217
214
|
state.status
|
218
|
-
|
219
215
|
rescue Interrupt => e
|
220
|
-
%i
|
216
|
+
%i[show poll].each do |action|
|
221
217
|
client.action_state.actions[action].reset
|
222
218
|
end
|
223
219
|
raise e
|
224
220
|
end
|
225
221
|
|
226
222
|
def self.cancel(client, id)
|
227
|
-
res = client.action_state.cancel(id, meta: {block: false})
|
223
|
+
res = client.action_state.cancel(id, meta: { block: false })
|
228
224
|
|
229
225
|
if res.ok? && res.action.blocking? && res.meta[:action_state_id]
|
230
226
|
res.meta[:action_state_id]
|
@@ -235,6 +231,7 @@ module HaveAPI::Client
|
|
235
231
|
end
|
236
232
|
|
237
233
|
private
|
234
|
+
|
238
235
|
def apply_args(args)
|
239
236
|
@prepared_path ||= @spec[:path].dup
|
240
237
|
@prepared_help ||= @spec[:help].dup
|
@@ -11,7 +11,7 @@ module HaveAPI::Client
|
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
14
|
-
attr_reader :progress
|
14
|
+
attr_reader :progress, :cancel_block
|
15
15
|
|
16
16
|
def initialize(response)
|
17
17
|
@data = response.response
|
@@ -26,7 +26,7 @@ module HaveAPI::Client
|
|
26
26
|
def status
|
27
27
|
@data[:status] === true
|
28
28
|
end
|
29
|
-
|
29
|
+
|
30
30
|
def finished?
|
31
31
|
@data[:finished] === true
|
32
32
|
end
|
@@ -40,7 +40,7 @@ module HaveAPI::Client
|
|
40
40
|
# is used only if the cancel operation is blocking.
|
41
41
|
def cancel(&block)
|
42
42
|
unless can_cancel?
|
43
|
-
|
43
|
+
raise "action ##{@data[:id]} (#{label}) cannot be cancelled"
|
44
44
|
end
|
45
45
|
|
46
46
|
@cancel = true
|
@@ -51,10 +51,6 @@ module HaveAPI::Client
|
|
51
51
|
@cancel === true
|
52
52
|
end
|
53
53
|
|
54
|
-
def cancel_block
|
55
|
-
@cancel_block
|
56
|
-
end
|
57
|
-
|
58
54
|
# Stop monitoring the action's state, the call from Action.wait_for_completion
|
59
55
|
# will return.
|
60
56
|
def stop
|
@@ -4,8 +4,9 @@ module HaveAPI::Client
|
|
4
4
|
module Authentication
|
5
5
|
# Raise this exception when authentication process fails somewhere
|
6
6
|
# outside action execution (in which access forbidden is raised from RestClient).
|
7
|
-
class AuthenticationFailed <
|
7
|
+
class AuthenticationFailed < StandardError
|
8
8
|
def initialize(msg)
|
9
|
+
super
|
9
10
|
@msg = msg
|
10
11
|
end
|
11
12
|
|
@@ -43,14 +44,10 @@ module HaveAPI::Client
|
|
43
44
|
end
|
44
45
|
|
45
46
|
# Called right after initialize. Use this method to initialize provider.
|
46
|
-
def setup
|
47
|
-
|
48
|
-
end
|
47
|
+
def setup; end
|
49
48
|
|
50
49
|
# Return RestClient::Resource instance. This is mainly for HTTP basic auth.
|
51
|
-
def resource
|
52
|
-
|
53
|
-
end
|
50
|
+
def resource; end
|
54
51
|
|
55
52
|
# Called for each request. Returns a hash of query parameters.
|
56
53
|
def request_query_params
|
@@ -8,7 +8,7 @@ module HaveAPI::Client::Authentication
|
|
8
8
|
RestClient::Resource.new(@communicator.url, @opts[:user], @opts[:password])
|
9
9
|
end
|
10
10
|
|
11
|
-
def user
|
12
|
-
def password
|
11
|
+
def user = @opts.[](:user)
|
12
|
+
def password = @opts.[](:password)
|
13
13
|
end
|
14
14
|
end
|
@@ -17,18 +17,20 @@ module HaveAPI::Client::Authentication
|
|
17
17
|
|
18
18
|
def request_query_params
|
19
19
|
return {} unless @configured
|
20
|
+
|
20
21
|
check_validity
|
21
|
-
@via == :query_param ? {@desc[:query_parameter] => @token} : {}
|
22
|
+
@via == :query_param ? { @desc[:query_parameter] => @token } : {}
|
22
23
|
end
|
23
24
|
|
24
25
|
def request_headers
|
25
26
|
return {} unless @configured
|
27
|
+
|
26
28
|
check_validity
|
27
|
-
@via == :header ? {@desc[:http_header] => @token} : {}
|
29
|
+
@via == :header ? { @desc[:http_header] => @token } : {}
|
28
30
|
end
|
29
31
|
|
30
32
|
def save
|
31
|
-
{token: @token, valid_to: @valid_to}
|
33
|
+
{ token: @token, valid_to: @valid_to }
|
32
34
|
end
|
33
35
|
|
34
36
|
def load(hash)
|
@@ -38,36 +40,37 @@ module HaveAPI::Client::Authentication
|
|
38
40
|
|
39
41
|
def renew
|
40
42
|
a = HaveAPI::Client::Action.new(
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
43
|
+
nil,
|
44
|
+
@communicator,
|
45
|
+
:renew,
|
46
|
+
@desc[:resources][:token][:actions][:renew],
|
47
|
+
[]
|
46
48
|
)
|
47
49
|
ret = HaveAPI::Client::Response.new(a, a.execute({}))
|
48
|
-
raise HaveAPI::Client::ActionFailed
|
50
|
+
raise HaveAPI::Client::ActionFailed, ret unless ret.ok?
|
49
51
|
|
50
52
|
@valid_to = ret[:valid_to]
|
51
|
-
@valid_to
|
53
|
+
@valid_to &&= DateTime.iso8601(@valid_to).to_time
|
52
54
|
end
|
53
55
|
|
54
56
|
def revoke
|
55
57
|
a = HaveAPI::Client::Action.new(
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
58
|
+
nil,
|
59
|
+
@communicator,
|
60
|
+
:revoke,
|
61
|
+
@desc[:resources][:token][:actions][:revoke],
|
62
|
+
[]
|
61
63
|
)
|
62
64
|
ret = HaveAPI::Client::Response.new(a, a.execute({}))
|
63
|
-
raise HaveAPI::Client::ActionFailed
|
65
|
+
raise HaveAPI::Client::ActionFailed, ret unless ret.ok?
|
64
66
|
end
|
65
67
|
|
66
68
|
protected
|
69
|
+
|
67
70
|
def request_token
|
68
71
|
input = {
|
69
72
|
lifetime: @opts[:lifetime],
|
70
|
-
interval: @opts[:interval] || 300
|
73
|
+
interval: @opts[:interval] || 300
|
71
74
|
}
|
72
75
|
request_credentials.each { |name| input[name] = @opts[name] if @opts[name] }
|
73
76
|
|
@@ -75,11 +78,11 @@ module HaveAPI::Client::Authentication
|
|
75
78
|
return if cont == :done
|
76
79
|
|
77
80
|
if @block.nil?
|
78
|
-
raise AuthenticationFailed
|
81
|
+
raise AuthenticationFailed, 'implement multi-factor authentication'
|
79
82
|
end
|
80
83
|
|
81
84
|
loop do
|
82
|
-
input = {token:
|
85
|
+
input = { token: }
|
83
86
|
input.update(@block.call(next_action, auth_action_input(next_action)))
|
84
87
|
|
85
88
|
cont, next_action, token = login_step(next_action, input)
|
@@ -99,7 +102,7 @@ module HaveAPI::Client::Authentication
|
|
99
102
|
resp = HaveAPI::Client::Response.new(a, a.execute(input))
|
100
103
|
|
101
104
|
if resp.failed?
|
102
|
-
raise AuthenticationFailed
|
105
|
+
raise AuthenticationFailed, resp.message || 'invalid credentials'
|
103
106
|
end
|
104
107
|
|
105
108
|
if resp[:complete]
|
@@ -112,21 +115,19 @@ module HaveAPI::Client::Authentication
|
|
112
115
|
end
|
113
116
|
|
114
117
|
def check_validity
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
+
return unless @valid_to && @valid_to < Time.now && @opts[:user] && @opts[:password]
|
119
|
+
|
120
|
+
request_token
|
118
121
|
end
|
119
122
|
|
120
123
|
def request_credentials
|
121
124
|
@desc[:resources][:token][:actions][:request][:input][:parameters].each_key.reject do |name|
|
122
|
-
%i
|
125
|
+
%i[interval lifetime].include?(name)
|
123
126
|
end
|
124
127
|
end
|
125
128
|
|
126
129
|
def auth_action_input(name)
|
127
|
-
@desc[:resources][:token][:actions][name][:input][:parameters].
|
128
|
-
%i(token).include?(k)
|
129
|
-
end
|
130
|
+
@desc[:resources][:token][:actions][name][:input][:parameters].except(:token)
|
130
131
|
end
|
131
132
|
end
|
132
133
|
end
|
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'pp'
|
2
|
-
|
3
1
|
# HaveAPI client interface.
|
4
2
|
class HaveAPI::Client::Client
|
5
3
|
attr_reader :resources
|
@@ -53,8 +51,8 @@ class HaveAPI::Client::Client
|
|
53
51
|
end
|
54
52
|
|
55
53
|
# See Communicator#authenticate.
|
56
|
-
def authenticate(auth_method, **options, &
|
57
|
-
@api.authenticate(auth_method, options, &
|
54
|
+
def authenticate(auth_method, **options, &)
|
55
|
+
@api.authenticate(auth_method, options, &)
|
58
56
|
end
|
59
57
|
|
60
58
|
# Get uthentication provider
|
@@ -92,20 +90,28 @@ class HaveAPI::Client::Client
|
|
92
90
|
|
93
91
|
# Initialize the client if it is not yet initialized and call the resource
|
94
92
|
# if it exists.
|
95
|
-
def method_missing(symbol, *
|
93
|
+
def method_missing(symbol, *)
|
96
94
|
return super(symbol, *args) if @setup
|
97
95
|
|
98
96
|
setup_api
|
99
97
|
|
100
98
|
if @resources.include?(symbol)
|
101
|
-
method(symbol).call(*
|
99
|
+
method(symbol).call(*)
|
102
100
|
|
103
101
|
else
|
104
102
|
super(symbol, *args)
|
105
103
|
end
|
106
104
|
end
|
107
105
|
|
106
|
+
def respond_to_missing?(symbol, *)
|
107
|
+
return super if @setup
|
108
|
+
|
109
|
+
setup_api
|
110
|
+
@resources.include?(symbol)
|
111
|
+
end
|
112
|
+
|
108
113
|
private
|
114
|
+
|
109
115
|
# Get the description from the API and setup resource methods.
|
110
116
|
def setup_api
|
111
117
|
@description = @api.describe_api(@version)
|