haveapi-client 0.26.5 → 0.27.0
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 +4 -0
- data/lib/haveapi/client/exceptions.rb +5 -1
- data/lib/haveapi/client/parameters/resource.rb +2 -0
- data/lib/haveapi/client/parameters/typed.rb +130 -35
- data/lib/haveapi/client/params.rb +5 -2
- data/lib/haveapi/client/version.rb +1 -1
- data/spec/integration/client_spec.rb +92 -0
- data/spec/integration/typed_input_spec.rb +113 -0
- data/spec/spec_helper.rb +24 -0
- data/spec/support/test_server.rb +95 -0
- metadata +5 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a6231f3629c9e015d7e916dc7cf97466b30849db2a1fe86a521f5fd1b99fbb72
|
|
4
|
+
data.tar.gz: 46cb500d05af0f1bdd25f4a2749d7d459ff8460d1477dea59b64e816162dc9e7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5afb270a053317c22c6a5748739423690743a02a1b3f8e58d1e045d33b4446bd0d96f5427ffc7c37f6cfbb3266b2cfd8801692d9f3c557843fe586e4a60a9535
|
|
7
|
+
data.tar.gz: e42801e205282f72038c3777d819799be33569e63a9864ef13786e7346785cca469dd6128f9c3241f257d098bd089c9f9b3d8d47473f778019b8c30361833207
|
data/Gemfile
CHANGED
|
@@ -5,7 +5,11 @@ module HaveAPI::Client
|
|
|
5
5
|
attr_reader :response
|
|
6
6
|
|
|
7
7
|
def initialize(response)
|
|
8
|
-
|
|
8
|
+
if response.respond_to?(:action)
|
|
9
|
+
super("#{response.action.name} failed: #{response.message}")
|
|
10
|
+
else
|
|
11
|
+
super(response.to_s)
|
|
12
|
+
end
|
|
9
13
|
|
|
10
14
|
@response = response
|
|
11
15
|
end
|
|
@@ -2,20 +2,6 @@ require 'date'
|
|
|
2
2
|
|
|
3
3
|
module HaveAPI::Client
|
|
4
4
|
class Parameters::Typed
|
|
5
|
-
module Boolean
|
|
6
|
-
def self.to_b(str)
|
|
7
|
-
return true if str === true
|
|
8
|
-
return false if str === false
|
|
9
|
-
|
|
10
|
-
if str.respond_to?(:=~)
|
|
11
|
-
return true if str =~ /^(true|t|yes|y|1)$/i
|
|
12
|
-
return false if str =~ /^(false|f|no|n|0)$/i
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
false
|
|
16
|
-
end
|
|
17
|
-
end
|
|
18
|
-
|
|
19
5
|
attr_reader :errors, :value
|
|
20
6
|
|
|
21
7
|
def initialize(params, desc, value)
|
|
@@ -26,6 +12,8 @@ module HaveAPI::Client
|
|
|
26
12
|
end
|
|
27
13
|
|
|
28
14
|
def valid?
|
|
15
|
+
return false unless @errors.empty?
|
|
16
|
+
|
|
29
17
|
ret = Validator.validate(@desc[:validators], @value, @params)
|
|
30
18
|
|
|
31
19
|
@errors.concat(ret) unless ret === true
|
|
@@ -44,39 +32,146 @@ module HaveAPI::Client
|
|
|
44
32
|
protected
|
|
45
33
|
|
|
46
34
|
def coerce(raw)
|
|
35
|
+
return nil if raw.nil?
|
|
36
|
+
|
|
47
37
|
type = @desc[:type]
|
|
48
38
|
|
|
49
|
-
|
|
50
|
-
|
|
39
|
+
case type
|
|
40
|
+
when 'Integer'
|
|
41
|
+
coerce_integer(raw)
|
|
42
|
+
when 'Float'
|
|
43
|
+
coerce_float(raw)
|
|
44
|
+
when 'Boolean'
|
|
45
|
+
coerce_boolean(raw)
|
|
46
|
+
when 'Datetime'
|
|
47
|
+
coerce_datetime(raw)
|
|
48
|
+
when 'String', 'Text'
|
|
49
|
+
coerce_string(raw)
|
|
50
|
+
else
|
|
51
|
+
raw
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
private
|
|
56
|
+
|
|
57
|
+
def coerce_integer(raw)
|
|
58
|
+
case raw
|
|
59
|
+
when ::Integer
|
|
60
|
+
raw
|
|
61
|
+
|
|
62
|
+
when ::Float
|
|
63
|
+
return raw.to_i if raw.finite? && raw == raw.to_i
|
|
64
|
+
|
|
65
|
+
invalid_integer
|
|
66
|
+
|
|
67
|
+
when ::String
|
|
68
|
+
s = raw.strip
|
|
69
|
+
return invalid_integer if s.empty?
|
|
70
|
+
return invalid_integer unless s.match?(/\A[+-]?\d+\z/)
|
|
51
71
|
|
|
52
|
-
|
|
53
|
-
raw.to_f
|
|
72
|
+
Integer(s, 10)
|
|
54
73
|
|
|
55
|
-
|
|
56
|
-
|
|
74
|
+
else
|
|
75
|
+
invalid_integer
|
|
76
|
+
end
|
|
77
|
+
rescue ArgumentError
|
|
78
|
+
invalid_integer
|
|
79
|
+
end
|
|
57
80
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
81
|
+
def coerce_float(raw)
|
|
82
|
+
if raw.is_a?(::Numeric)
|
|
83
|
+
value = raw.to_f
|
|
84
|
+
return value if value.finite?
|
|
61
85
|
|
|
62
|
-
|
|
63
|
-
|
|
86
|
+
@errors << 'not a valid float'
|
|
87
|
+
nil
|
|
64
88
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
rescue ArgumentError
|
|
69
|
-
@errors << 'not in ISO 8601 format'
|
|
70
|
-
nil
|
|
71
|
-
end
|
|
72
|
-
end
|
|
89
|
+
elsif raw.is_a?(::String)
|
|
90
|
+
s = raw.strip
|
|
91
|
+
return invalid_float if s.empty?
|
|
73
92
|
|
|
74
|
-
|
|
75
|
-
|
|
93
|
+
value = Float(s)
|
|
94
|
+
return value if value.finite?
|
|
95
|
+
|
|
96
|
+
invalid_float
|
|
76
97
|
|
|
77
98
|
else
|
|
99
|
+
invalid_float
|
|
100
|
+
end
|
|
101
|
+
rescue ArgumentError
|
|
102
|
+
invalid_float
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def coerce_boolean(raw)
|
|
106
|
+
return raw if [true, false].include?(raw)
|
|
107
|
+
|
|
108
|
+
if raw.is_a?(::Integer)
|
|
109
|
+
return true if raw == 1
|
|
110
|
+
return false if raw == 0
|
|
111
|
+
|
|
112
|
+
return invalid_boolean
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
if raw.is_a?(::String)
|
|
116
|
+
s = raw.strip.downcase
|
|
117
|
+
return invalid_boolean if s.empty?
|
|
118
|
+
|
|
119
|
+
return true if %w[true t yes y 1].include?(s)
|
|
120
|
+
return false if %w[false f no n 0].include?(s)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
invalid_boolean
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def coerce_datetime(raw)
|
|
127
|
+
case raw
|
|
128
|
+
when ::Time
|
|
78
129
|
raw
|
|
130
|
+
|
|
131
|
+
when ::Date, ::DateTime
|
|
132
|
+
raw.to_time
|
|
133
|
+
|
|
134
|
+
when ::String
|
|
135
|
+
return invalid_datetime if raw.strip.empty?
|
|
136
|
+
|
|
137
|
+
DateTime.iso8601(raw).to_time
|
|
138
|
+
|
|
139
|
+
else
|
|
140
|
+
invalid_datetime
|
|
79
141
|
end
|
|
142
|
+
rescue ArgumentError
|
|
143
|
+
invalid_datetime
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def coerce_string(raw)
|
|
147
|
+
return invalid_string if raw.is_a?(::Array) || raw.is_a?(::Hash)
|
|
148
|
+
|
|
149
|
+
raw.to_s
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def invalid_integer
|
|
153
|
+
@errors << 'not a valid integer'
|
|
154
|
+
nil
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def invalid_float
|
|
158
|
+
@errors << 'not a valid float'
|
|
159
|
+
nil
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def invalid_boolean
|
|
163
|
+
@errors << 'not a valid boolean'
|
|
164
|
+
nil
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def invalid_datetime
|
|
168
|
+
@errors << 'not in ISO 8601 format'
|
|
169
|
+
nil
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def invalid_string
|
|
173
|
+
@errors << 'not a valid string'
|
|
174
|
+
nil
|
|
80
175
|
end
|
|
81
176
|
end
|
|
82
177
|
end
|
|
@@ -29,9 +29,12 @@ module HaveAPI::Client
|
|
|
29
29
|
@action.input_params.each do |name, p|
|
|
30
30
|
next if p[:validators].nil?
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
presence_validator =
|
|
33
|
+
p[:validators][:presence] || p[:validators][:present] || p[:validators][:required]
|
|
34
34
|
|
|
35
|
+
if presence_validator && @params[name].nil?
|
|
36
|
+
error(name, 'required parameter missing')
|
|
37
|
+
next
|
|
35
38
|
elsif @params[name].nil?
|
|
36
39
|
next
|
|
37
40
|
end
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
RSpec.describe HaveAPI::Client::Client do
|
|
6
|
+
let(:base_url) { TEST_SERVER.base_url }
|
|
7
|
+
|
|
8
|
+
it 'exposes versions and compatibility' do
|
|
9
|
+
client = described_class.new(base_url)
|
|
10
|
+
|
|
11
|
+
versions = client.versions
|
|
12
|
+
expect(versions[:default]).to eq('1.0')
|
|
13
|
+
expect(versions[:versions]).to include('1.0')
|
|
14
|
+
expect(client.compatible?).to(
|
|
15
|
+
satisfy { |value| [:compatible, :imperfect, false].include?(value) }
|
|
16
|
+
)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it 'filters docs until authenticated' do
|
|
20
|
+
unauthenticated = described_class.new(base_url)
|
|
21
|
+
unauthenticated.setup
|
|
22
|
+
|
|
23
|
+
expect { unauthenticated.project }.to raise_error(NoMethodError)
|
|
24
|
+
|
|
25
|
+
authenticated = described_class.new(base_url)
|
|
26
|
+
authenticated.authenticate(:basic, user: 'user', password: 'pass')
|
|
27
|
+
|
|
28
|
+
expect { authenticated.project }.not_to raise_error
|
|
29
|
+
expect(authenticated.project).to be_a(HaveAPI::Client::Resource)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it 'supports CRUD flow for projects' do
|
|
33
|
+
client = described_class.new(base_url)
|
|
34
|
+
client.authenticate(:basic, user: 'user', password: 'pass')
|
|
35
|
+
|
|
36
|
+
projects = client.project.list
|
|
37
|
+
expect(projects).to be_a(Array)
|
|
38
|
+
expect(projects.size).to be >= 2
|
|
39
|
+
|
|
40
|
+
project = client.project.find(projects.first.id)
|
|
41
|
+
expect(project.id).to eq(projects.first.id)
|
|
42
|
+
|
|
43
|
+
created = client.project.create(name: 'Gamma')
|
|
44
|
+
expect(created.id).to be_a(Integer)
|
|
45
|
+
expect(created.name).to eq('Gamma')
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it 'handles nested task resources' do
|
|
49
|
+
client = described_class.new(base_url)
|
|
50
|
+
client.authenticate(:basic, user: 'user', password: 'pass')
|
|
51
|
+
|
|
52
|
+
project = client.project.list.first
|
|
53
|
+
tasks = client.project.task.list(project.id)
|
|
54
|
+
expect(tasks).to be_a(Array)
|
|
55
|
+
|
|
56
|
+
task = client.project(project.id).task.create(label: 'Do it')
|
|
57
|
+
expect(task.id).to be_a(Integer)
|
|
58
|
+
expect(task.label).to eq('Do it')
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
it 'raises validation error on missing required params' do
|
|
62
|
+
client = described_class.new(base_url)
|
|
63
|
+
client.authenticate(:basic, user: 'user', password: 'pass')
|
|
64
|
+
|
|
65
|
+
expect { client.project.create({}) }
|
|
66
|
+
.to raise_error(HaveAPI::Client::ValidationError)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
it 'supports blocking actions and action state polling' do
|
|
70
|
+
client = described_class.new(base_url)
|
|
71
|
+
client.authenticate(:basic, user: 'user', password: 'pass')
|
|
72
|
+
|
|
73
|
+
project = client.project.create(name: 'Blocky')
|
|
74
|
+
task = client.project(project.id).task.create(label: 'Run me')
|
|
75
|
+
|
|
76
|
+
response = client.project.task.run(project.id, task.id, meta: { block: false })
|
|
77
|
+
expect(response.meta[:action_state_id]).to be_a(Integer)
|
|
78
|
+
|
|
79
|
+
result = response.wait_for_completion(interval: 0.05, update_in: 0.05, timeout: 2)
|
|
80
|
+
expect(result).to be(true)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
it 'surfaces HaveAPI errors as ActionFailed' do
|
|
84
|
+
client = described_class.new(base_url)
|
|
85
|
+
|
|
86
|
+
expect { client.test.fail }
|
|
87
|
+
.to raise_error(HaveAPI::Client::ActionFailed) do |err|
|
|
88
|
+
expect(err.response.message).to eq('forced failure')
|
|
89
|
+
expect(err.response.errors).to include(:base)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
RSpec.describe HaveAPI::Client::Client do
|
|
6
|
+
let(:client) { described_class.new(TEST_SERVER.base_url) }
|
|
7
|
+
let(:valid_params) do
|
|
8
|
+
{
|
|
9
|
+
i: 1,
|
|
10
|
+
f: 1.0,
|
|
11
|
+
b: true,
|
|
12
|
+
dt: '2020-01-01T00:00:00Z',
|
|
13
|
+
s: 'x',
|
|
14
|
+
t: 'y'
|
|
15
|
+
}
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it 'coerces valid typed inputs' do
|
|
19
|
+
res = client.test.echo(
|
|
20
|
+
i: ' 42 ',
|
|
21
|
+
f: 5,
|
|
22
|
+
b: 'yes',
|
|
23
|
+
dt: '2020-01-01T00:00:00Z',
|
|
24
|
+
s: 123,
|
|
25
|
+
t: false
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
expect(res).to be_a(HaveAPI::Client::Response)
|
|
29
|
+
expect(res[:i]).to eq(42)
|
|
30
|
+
expect(res[:f]).to eq(5.0)
|
|
31
|
+
expect(res[:b]).to be(true)
|
|
32
|
+
expect(res[:dt]).to match(/\A2020-01-01T00:00:00(?:Z|\+00:00)\z/)
|
|
33
|
+
expect(res[:s]).to eq('123')
|
|
34
|
+
expect(res[:t]).to eq('false')
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it 'accepts exponent float strings' do
|
|
38
|
+
res = client.test.echo(
|
|
39
|
+
i: 1,
|
|
40
|
+
f: '1e3',
|
|
41
|
+
b: true,
|
|
42
|
+
dt: '2020-01-01',
|
|
43
|
+
s: 'ok',
|
|
44
|
+
t: 'ok'
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
expect(res[:f]).to eq(1000.0)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it 'rejects invalid integer strings' do
|
|
51
|
+
expect { client.test.echo(valid_params.merge(i: 'abc')) }
|
|
52
|
+
.to raise_error(HaveAPI::Client::ValidationError) do |err|
|
|
53
|
+
expect(err.errors).to include(:i)
|
|
54
|
+
expect(err.errors[:i]).to include(a_string_matching(/not a valid integer/))
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
it 'rejects non-integral floats for integers' do
|
|
59
|
+
expect { client.test.echo(valid_params.merge(i: 12.3)) }
|
|
60
|
+
.to raise_error(HaveAPI::Client::ValidationError) do |err|
|
|
61
|
+
expect(err.errors).to include(:i)
|
|
62
|
+
expect(err.errors[:i]).to include(a_string_matching(/not a valid integer/))
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
it 'rejects invalid floats' do
|
|
67
|
+
expect { client.test.echo(valid_params.merge(f: 'abc')) }
|
|
68
|
+
.to raise_error(HaveAPI::Client::ValidationError) do |err|
|
|
69
|
+
expect(err.errors).to include(:f)
|
|
70
|
+
expect(err.errors[:f]).to include(a_string_matching(/not a valid float/))
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
it 'rejects invalid boolean strings' do
|
|
75
|
+
expect { client.test.echo(valid_params.merge(b: 'maybe')) }
|
|
76
|
+
.to raise_error(HaveAPI::Client::ValidationError) do |err|
|
|
77
|
+
expect(err.errors).to include(:b)
|
|
78
|
+
expect(err.errors[:b]).to include(a_string_matching(/not a valid boolean/))
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
it 'rejects invalid boolean integers' do
|
|
83
|
+
expect { client.test.echo(valid_params.merge(b: 2)) }
|
|
84
|
+
.to raise_error(HaveAPI::Client::ValidationError) do |err|
|
|
85
|
+
expect(err.errors).to include(:b)
|
|
86
|
+
expect(err.errors[:b]).to include(a_string_matching(/not a valid boolean/))
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
it 'rejects invalid datetimes' do
|
|
91
|
+
expect { client.test.echo(valid_params.merge(dt: 'yesterday')) }
|
|
92
|
+
.to raise_error(HaveAPI::Client::ValidationError) do |err|
|
|
93
|
+
expect(err.errors).to include(:dt)
|
|
94
|
+
expect(err.errors[:dt]).to include(a_string_matching(/ISO 8601/))
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
it 'rejects arrays for string params' do
|
|
99
|
+
expect { client.test.echo(valid_params.merge(s: [1, 2])) }
|
|
100
|
+
.to raise_error(HaveAPI::Client::ValidationError) do |err|
|
|
101
|
+
expect(err.errors).to include(:s)
|
|
102
|
+
expect(err.errors[:s]).to include(a_string_matching(/not a valid string/))
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
it 'rejects hashes for text params' do
|
|
107
|
+
expect { client.test.echo(valid_params.merge(t: { a: 1 })) }
|
|
108
|
+
.to raise_error(HaveAPI::Client::ValidationError) do |err|
|
|
109
|
+
expect(err.errors).to include(:t)
|
|
110
|
+
expect(err.errors[:t]).to include(a_string_matching(/not a valid string/))
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
data/spec/spec_helper.rb
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'bundler/setup'
|
|
4
|
+
require 'haveapi/client'
|
|
5
|
+
|
|
6
|
+
require_relative 'support/test_server'
|
|
7
|
+
|
|
8
|
+
TEST_SERVER = ClientTestServer.new
|
|
9
|
+
|
|
10
|
+
RSpec.configure do |config|
|
|
11
|
+
config.order = :random
|
|
12
|
+
|
|
13
|
+
config.before(:suite) do
|
|
14
|
+
TEST_SERVER.start
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
config.after(:suite) do
|
|
18
|
+
TEST_SERVER.stop!
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
config.before do
|
|
22
|
+
TEST_SERVER.reset!
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'open3'
|
|
4
|
+
require 'net/http'
|
|
5
|
+
require 'timeout'
|
|
6
|
+
require 'uri'
|
|
7
|
+
|
|
8
|
+
class ClientTestServer
|
|
9
|
+
READY_PREFIX = 'HAVEAPI_TEST_SERVER_READY'
|
|
10
|
+
|
|
11
|
+
attr_reader :base_url
|
|
12
|
+
|
|
13
|
+
def initialize
|
|
14
|
+
@root = File.expand_path('../../../..', __dir__)
|
|
15
|
+
@server_script = File.join(@root, 'servers', 'ruby', 'test_support', 'client_test_server.rb')
|
|
16
|
+
@gemfile = File.join(@root, 'servers', 'ruby', 'Gemfile')
|
|
17
|
+
@cwd = File.join(@root, 'clients', 'ruby')
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def start
|
|
21
|
+
return if @wait_thr
|
|
22
|
+
|
|
23
|
+
env = { 'BUNDLE_GEMFILE' => @gemfile }
|
|
24
|
+
cmd = ['bundle', 'exec', 'ruby', @server_script, '--port', '0']
|
|
25
|
+
@stdin, @stdout, @wait_thr = Open3.popen2e(env, *cmd, chdir: @cwd)
|
|
26
|
+
|
|
27
|
+
read_ready!
|
|
28
|
+
wait_for_health!
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def reset!
|
|
32
|
+
ensure_started!
|
|
33
|
+
|
|
34
|
+
uri = URI.join(@base_url, '/__reset')
|
|
35
|
+
req = Net::HTTP::Post.new(uri)
|
|
36
|
+
req['Content-Type'] = 'application/json'
|
|
37
|
+
res = Net::HTTP.start(uri.host, uri.port) { |http| http.request(req) }
|
|
38
|
+
|
|
39
|
+
return if res.is_a?(Net::HTTPSuccess)
|
|
40
|
+
|
|
41
|
+
raise "reset failed: #{res.code} #{res.body}"
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def stop!
|
|
45
|
+
return unless @wait_thr
|
|
46
|
+
|
|
47
|
+
Process.kill('TERM', @wait_thr.pid)
|
|
48
|
+
@wait_thr.value
|
|
49
|
+
rescue Errno::ESRCH
|
|
50
|
+
nil
|
|
51
|
+
ensure
|
|
52
|
+
@stdin&.close
|
|
53
|
+
@stdout&.close
|
|
54
|
+
@wait_thr = nil
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
private
|
|
58
|
+
|
|
59
|
+
def ensure_started!
|
|
60
|
+
start unless @wait_thr
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def read_ready!
|
|
64
|
+
Timeout.timeout(10) do
|
|
65
|
+
while (line = @stdout.gets)
|
|
66
|
+
next unless line.include?(READY_PREFIX)
|
|
67
|
+
|
|
68
|
+
@base_url = line.split.last&.strip
|
|
69
|
+
break
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
raise 'server did not start' unless @base_url
|
|
74
|
+
rescue Timeout::Error
|
|
75
|
+
raise 'server did not start in time'
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def wait_for_health!
|
|
79
|
+
Timeout.timeout(5) do
|
|
80
|
+
loop do
|
|
81
|
+
begin
|
|
82
|
+
uri = URI.join(@base_url, '/__health')
|
|
83
|
+
res = Net::HTTP.get_response(uri)
|
|
84
|
+
return if res.is_a?(Net::HTTPSuccess)
|
|
85
|
+
rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH, IOError
|
|
86
|
+
# retry
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
sleep 0.05
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
rescue Timeout::Error
|
|
93
|
+
raise 'server did not become healthy in time'
|
|
94
|
+
end
|
|
95
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: haveapi-client
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.27.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jakub Skokan
|
|
@@ -151,6 +151,10 @@ files:
|
|
|
151
151
|
- lib/haveapi/client/version.rb
|
|
152
152
|
- lib/restclient_ext/resource.rb
|
|
153
153
|
- shell.nix
|
|
154
|
+
- spec/integration/client_spec.rb
|
|
155
|
+
- spec/integration/typed_input_spec.rb
|
|
156
|
+
- spec/spec_helper.rb
|
|
157
|
+
- spec/support/test_server.rb
|
|
154
158
|
homepage: ''
|
|
155
159
|
licenses:
|
|
156
160
|
- MIT
|