haveapi-client 0.20.0 → 0.21.1
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/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)
|