killbill-client 4.0.6 → 4.0.7
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/.github/workflows/ci.yml +16 -15
- data/docker/docker-compose.ci.mysql.yml +2 -2
- data/docker/docker-compose.ci.postgresql.yml +2 -2
- data/lib/killbill_client/api/net_http_adapter.rb +98 -7
- data/lib/killbill_client/models/invoice.rb +1 -1
- data/lib/killbill_client/version.rb +1 -1
- data/spec/killbill_client/http_adapter_spec.rb +250 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8e86e4cbc6c49a01a442aeb497caacd4a7893948
|
4
|
+
data.tar.gz: f02a33231d29d69f6be5b5f8987b87cc7f62514f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a5d298382c7555f66ce46ceae00be264f335a419682671ec1233c9c3c22aa706f318563a287b270c638e815c8d09f8e152f9c6226ce6b349623762706d673ec4
|
7
|
+
data.tar.gz: 8de6e6aea0a680b4301c1d8cb688a89b939ce68ee7b40b12f1acf34986b5e2d74dc79c5679046339c10a4d039bc7ed47eb027587070870658bfeec3c318cf178
|
data/.github/workflows/ci.yml
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
name: ci
|
2
2
|
|
3
3
|
on:
|
4
|
-
push
|
5
|
-
workflow_dispatch
|
4
|
+
- push
|
5
|
+
- workflow_dispatch
|
6
|
+
- pull_request
|
6
7
|
|
7
8
|
env:
|
8
9
|
COMPOSE_DOCKER_CLI_BUILD: 1
|
@@ -15,11 +16,11 @@ env:
|
|
15
16
|
|
16
17
|
jobs:
|
17
18
|
test:
|
18
|
-
runs-on: ubuntu-
|
19
|
+
runs-on: ubuntu-22.04
|
19
20
|
strategy:
|
20
21
|
matrix:
|
21
22
|
include:
|
22
|
-
- ruby-version: '2.
|
23
|
+
- ruby-version: '3.2.2'
|
23
24
|
database-adapter: 'mysql2'
|
24
25
|
database-user: 'root'
|
25
26
|
database-password: 'root'
|
@@ -31,13 +32,13 @@ jobs:
|
|
31
32
|
database-password: 'postgres'
|
32
33
|
database-port: '5432'
|
33
34
|
docker-compose-file: 'docker-compose.ci.postgresql.yml'
|
34
|
-
- ruby-version: 'jruby-9.
|
35
|
+
- ruby-version: 'jruby-9.4.2.0'
|
35
36
|
database-adapter: 'mysql2'
|
36
37
|
database-user: 'root'
|
37
38
|
database-password: 'root'
|
38
39
|
database-port: '3306'
|
39
40
|
docker-compose-file: 'docker-compose.ci.mysql.yml'
|
40
|
-
- ruby-version: '
|
41
|
+
- ruby-version: '3.3.5'
|
41
42
|
database-adapter: 'postgresql'
|
42
43
|
database-user: 'postgres'
|
43
44
|
database-password: 'postgres'
|
@@ -49,7 +50,7 @@ jobs:
|
|
49
50
|
database-password: 'postgres'
|
50
51
|
database-port: '5432'
|
51
52
|
docker-compose-file: 'docker-compose.ci.postgresql.yml'
|
52
|
-
- ruby-version: 'jruby-9.
|
53
|
+
- ruby-version: 'jruby-9.4.2.0'
|
53
54
|
database-adapter: 'postgresql'
|
54
55
|
database-user: 'postgres'
|
55
56
|
database-password: 'postgres'
|
@@ -57,7 +58,7 @@ jobs:
|
|
57
58
|
docker-compose-file: 'docker-compose.ci.postgresql.yml'
|
58
59
|
steps:
|
59
60
|
- name: Checkout code
|
60
|
-
uses: actions/checkout@
|
61
|
+
uses: actions/checkout@v4
|
61
62
|
- name: Set up Ruby
|
62
63
|
uses: ruby/setup-ruby@v1
|
63
64
|
with:
|
@@ -66,8 +67,8 @@ jobs:
|
|
66
67
|
- name: Start stack
|
67
68
|
run: |
|
68
69
|
cd docker
|
69
|
-
docker
|
70
|
-
docker start
|
70
|
+
docker compose -p it -f ${{ matrix.docker-compose-file }} up --no-start
|
71
|
+
docker start it-db-1
|
71
72
|
- name: Wait for MySQL
|
72
73
|
if: ${{ matrix.docker-compose-file == 'docker-compose.ci.mysql.yml' }}
|
73
74
|
run: |
|
@@ -100,7 +101,7 @@ jobs:
|
|
100
101
|
# Sometimes it gets stuck (if Kill Bill starts when the DB isn't ready?)
|
101
102
|
timeout-minutes: 4
|
102
103
|
run: |
|
103
|
-
docker start
|
104
|
+
docker start it-killbill-1
|
104
105
|
count=0
|
105
106
|
until $(curl --connect-timeout 10 --max-time 30 --output /dev/null --silent --fail http://${KB_ADDRESS}:${KB_PORT}/1.0/healthcheck); do
|
106
107
|
if [[ "$count" == "180" ]]; then
|
@@ -148,10 +149,10 @@ jobs:
|
|
148
149
|
echo "[DEBUG] docker ps -a"
|
149
150
|
docker ps -a
|
150
151
|
echo "[DEBUG] killbill env"
|
151
|
-
docker exec
|
152
|
+
docker exec it-killbill-1 env || true
|
152
153
|
echo "[DEBUG] db env"
|
153
|
-
docker exec
|
154
|
+
docker exec it-db-1 env || true
|
154
155
|
echo "[DEBUG] killbill logs"
|
155
|
-
docker logs -t --details
|
156
|
+
docker logs -t --details it-killbill-1 || true
|
156
157
|
echo "[DEBUG] db logs"
|
157
|
-
docker logs -t --details
|
158
|
+
docker logs -t --details it-db-1 || true
|
@@ -3,7 +3,7 @@ version: '3.8'
|
|
3
3
|
services:
|
4
4
|
killbill:
|
5
5
|
network_mode: host
|
6
|
-
image: killbill/killbill:0.
|
6
|
+
image: killbill/killbill:0.24.0
|
7
7
|
environment:
|
8
8
|
- KILLBILL_CATALOG_URI=SpyCarAdvanced.xml
|
9
9
|
- KILLBILL_DAO_URL=jdbc:mysql://127.0.0.1:3306/killbill
|
@@ -16,6 +16,6 @@ services:
|
|
16
16
|
- db
|
17
17
|
db:
|
18
18
|
network_mode: host
|
19
|
-
image: killbill/mariadb:0.
|
19
|
+
image: killbill/mariadb:0.24
|
20
20
|
environment:
|
21
21
|
- MYSQL_ROOT_PASSWORD=root
|
@@ -3,7 +3,7 @@ version: '3.8'
|
|
3
3
|
services:
|
4
4
|
killbill:
|
5
5
|
network_mode: host
|
6
|
-
image: killbill/killbill:0.
|
6
|
+
image: killbill/killbill:0.24.0
|
7
7
|
environment:
|
8
8
|
- KILLBILL_CATALOG_URI=SpyCarAdvanced.xml
|
9
9
|
- KILLBILL_DAO_URL=jdbc:postgresql://127.0.0.1:5432/killbill
|
@@ -16,6 +16,6 @@ services:
|
|
16
16
|
- db
|
17
17
|
db:
|
18
18
|
network_mode: host
|
19
|
-
image: killbill/postgresql:0.
|
19
|
+
image: killbill/postgresql:0.24
|
20
20
|
environment:
|
21
21
|
- POSTGRES_PASSWORD=postgres
|
@@ -36,9 +36,79 @@ module KillBillClient
|
|
36
36
|
}
|
37
37
|
|
38
38
|
def build_uri(relative_uri, options)
|
39
|
-
#
|
40
|
-
|
41
|
-
|
39
|
+
# Split the URI into path and query parts
|
40
|
+
uri_parts = relative_uri.split('?', 2)
|
41
|
+
path_part = uri_parts[0]
|
42
|
+
query_part = uri_parts[1]
|
43
|
+
|
44
|
+
# Check if this is an absolute URI (has scheme) by looking for protocol pattern
|
45
|
+
is_absolute_uri = path_part.match?(/\A[a-z][a-z0-9+.-]*:\/\//i)
|
46
|
+
|
47
|
+
if is_absolute_uri
|
48
|
+
# This is an absolute URI, parse it carefully
|
49
|
+
begin
|
50
|
+
# Parse the URI components manually to handle spaces properly
|
51
|
+
if path_part.match(/\A([a-z][a-z0-9+.-]*):\/\/([^\/]+)(\/.*)?/i)
|
52
|
+
scheme = $1
|
53
|
+
authority = $2 # host:port
|
54
|
+
path = $3 || '/'
|
55
|
+
|
56
|
+
# Encode only the path segments, not the scheme or authority
|
57
|
+
if path && path != '/'
|
58
|
+
path_segments = path.split('/')
|
59
|
+
encoded_segments = path_segments.map do |segment|
|
60
|
+
# Skip encoding if the segment is already encoded (contains %XX patterns)
|
61
|
+
if segment.match?(/%[0-9A-Fa-f]{2}/)
|
62
|
+
segment
|
63
|
+
else
|
64
|
+
unsafe_regex = /[^a-zA-Z0-9\-_.!~*'()]/
|
65
|
+
if unsafe_regex.match?(segment)
|
66
|
+
CGI.escape(segment).gsub('+', '%20')
|
67
|
+
else
|
68
|
+
segment
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
encoded_path = encoded_segments.join('/')
|
73
|
+
else
|
74
|
+
encoded_path = path
|
75
|
+
end
|
76
|
+
|
77
|
+
encoded_relative_uri = "#{scheme}://#{authority}#{encoded_path}"
|
78
|
+
encoded_relative_uri += "?#{query_part}" if query_part
|
79
|
+
else
|
80
|
+
# Fallback: treat as relative if parsing fails
|
81
|
+
is_absolute_uri = false
|
82
|
+
end
|
83
|
+
rescue
|
84
|
+
# Fallback: treat as relative if any error occurs
|
85
|
+
is_absolute_uri = false
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
unless is_absolute_uri
|
90
|
+
# This is a relative URI, encode path segments individually
|
91
|
+
path_segments = path_part.split('/')
|
92
|
+
encoded_segments = path_segments.map do |segment|
|
93
|
+
# Skip encoding if the segment is already encoded (contains %XX patterns)
|
94
|
+
if segment.match?(/%[0-9A-Fa-f]{2}/)
|
95
|
+
segment
|
96
|
+
else
|
97
|
+
# Only encode segments that contain unsafe characters
|
98
|
+
unsafe_regex = /[^a-zA-Z0-9\-_.!~*'()]/
|
99
|
+
if unsafe_regex.match?(segment)
|
100
|
+
# Use CGI.escape and replace + with %20 for URL path encoding
|
101
|
+
CGI.escape(segment).gsub('+', '%20')
|
102
|
+
else
|
103
|
+
segment
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
encoded_path = encoded_segments.join('/')
|
108
|
+
encoded_relative_uri = query_part ? "#{encoded_path}?#{query_part}" : encoded_path
|
109
|
+
end
|
110
|
+
|
111
|
+
if !is_absolute_uri
|
42
112
|
uri = (options[:base_uri] || KillBillClient::API.base_uri)
|
43
113
|
uri = URI.parse(uri) unless uri.is_a?(URI)
|
44
114
|
# Note: make sure to keep the full path (if any) from URI::HTTP, for non-ROOT deployments
|
@@ -49,7 +119,19 @@ module KillBillClient
|
|
49
119
|
uri = encoded_relative_uri
|
50
120
|
uri = URI.parse(uri) unless uri.is_a?(URI)
|
51
121
|
end
|
52
|
-
|
122
|
+
|
123
|
+
query_params = encode_params(options)
|
124
|
+
if query_params && !query_params.empty?
|
125
|
+
# encode_params returns "?param=value", so remove the leading "?"
|
126
|
+
params_without_question = query_params[1..-1]
|
127
|
+
if uri.query && !uri.query.empty?
|
128
|
+
# If there's already a query string, append with &
|
129
|
+
uri.query = uri.query + '&' + params_without_question
|
130
|
+
else
|
131
|
+
# If no existing query string, set it directly
|
132
|
+
uri.query = params_without_question
|
133
|
+
end
|
134
|
+
end
|
53
135
|
|
54
136
|
uri
|
55
137
|
end
|
@@ -59,17 +141,25 @@ module KillBillClient
|
|
59
141
|
# so remove with from global hash and insert them under :params
|
60
142
|
plugin_properties = options.delete :pluginProperty
|
61
143
|
if plugin_properties && plugin_properties.size > 0
|
144
|
+
options[:params] ||= {}
|
62
145
|
options[:params][:pluginProperty] = plugin_properties.map { |p| "#{CGI.escape p.key.to_s}=#{CGI.escape p.value.to_s}" }
|
63
146
|
end
|
64
147
|
|
65
148
|
control_plugin_names = options.delete(:controlPluginNames)
|
66
|
-
|
149
|
+
if control_plugin_names
|
150
|
+
options[:params] ||= {}
|
151
|
+
options[:params][:controlPluginName] = control_plugin_names
|
152
|
+
end
|
67
153
|
|
68
154
|
return nil unless (options[:params] && !options[:params].empty?)
|
69
155
|
|
70
|
-
|
156
|
+
if (options[:return_full_stacktraces] || KillBillClient.return_full_stacktraces)
|
157
|
+
options[:params][:withStackTrace] = true
|
158
|
+
end
|
71
159
|
|
72
160
|
pairs = options[:params].map { |key, value|
|
161
|
+
next if value.nil?
|
162
|
+
|
73
163
|
# If the value is an array, we 'demultiplex' into several
|
74
164
|
if value.is_a? Array
|
75
165
|
internal_pairs = value.map do |simple_value|
|
@@ -79,8 +169,9 @@ module KillBillClient
|
|
79
169
|
else
|
80
170
|
"#{CGI.escape key.to_s}=#{CGI.escape value.to_s}"
|
81
171
|
end
|
82
|
-
}
|
172
|
+
}.compact
|
83
173
|
pairs.flatten!
|
174
|
+
return nil if pairs.empty?
|
84
175
|
"?#{pairs.join '&'}"
|
85
176
|
end
|
86
177
|
|
@@ -212,7 +212,7 @@ module KillBillClient
|
|
212
212
|
def get_invoice_template(is_manual_pay, locale = nil, options = {})
|
213
213
|
|
214
214
|
require_multi_tenant_options!(options, "Retrieving an invoice template supported in multi-tenant mode")
|
215
|
-
|
215
|
+
locale ||= 'en'
|
216
216
|
|
217
217
|
get "#{KILLBILL_API_INVOICES_PREFIX}/#{is_manual_pay ? "manualPayTemplate/#{locale}" : "template"}",
|
218
218
|
{},
|
@@ -106,6 +106,256 @@ describe KillBillClient::API do
|
|
106
106
|
uri = http_adapter.send(:build_uri, KillBillClient::Model::Account::KILLBILL_API_ACCOUNTS_PREFIX, options)
|
107
107
|
expect(uri).to eq(URI.parse("#{KillBillClient::API.base_uri.to_s}/1.0/kb/accounts"))
|
108
108
|
end
|
109
|
+
|
110
|
+
describe '#build_uri' do
|
111
|
+
let(:http_adapter) { DummyForHTTPAdapter.new }
|
112
|
+
|
113
|
+
before do
|
114
|
+
KillBillClient.url = 'http://example.com:8080'
|
115
|
+
end
|
116
|
+
|
117
|
+
context 'with basic relative URI' do
|
118
|
+
it 'should combine base URI with relative URI' do
|
119
|
+
relative_uri = '/1.0/kb/accounts'
|
120
|
+
options = {}
|
121
|
+
uri = http_adapter.send(:build_uri, relative_uri, options)
|
122
|
+
expect(uri.to_s).to eq('http://example.com:8080/1.0/kb/accounts')
|
123
|
+
end
|
124
|
+
|
125
|
+
it 'should handle relative URI without leading slash' do
|
126
|
+
relative_uri = '1.0/kb/accounts'
|
127
|
+
options = {}
|
128
|
+
uri = http_adapter.send(:build_uri, relative_uri, options)
|
129
|
+
expect(uri.to_s).to eq('http://example.com:8080/1.0/kb/accounts')
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
context 'with custom base_uri in options' do
|
134
|
+
it 'should use custom base_uri from options' do
|
135
|
+
relative_uri = '/1.0/kb/accounts'
|
136
|
+
options = { base_uri: 'https://custom.example.com:9090' }
|
137
|
+
uri = http_adapter.send(:build_uri, relative_uri, options)
|
138
|
+
expect(uri.to_s).to eq('https://custom.example.com:9090/1.0/kb/accounts')
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
context 'with absolute URI' do
|
143
|
+
it 'should use absolute URI as-is' do
|
144
|
+
relative_uri = 'https://absolute.example.com/api/test'
|
145
|
+
options = {}
|
146
|
+
uri = http_adapter.send(:build_uri, relative_uri, options)
|
147
|
+
expect(uri.to_s).to eq('https://absolute.example.com/api/test')
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
context 'with path encoding' do
|
152
|
+
it 'should handle already encoded URIs without double encoding' do
|
153
|
+
relative_uri = '/1.0/kb/accounts/my%20account%20with%20spaces'
|
154
|
+
options = {}
|
155
|
+
uri = http_adapter.send(:build_uri, relative_uri, options)
|
156
|
+
expect(uri.to_s).to eq('http://example.com:8080/1.0/kb/accounts/my%20account%20with%20spaces')
|
157
|
+
end
|
158
|
+
|
159
|
+
it 'should not encode safe characters in path' do
|
160
|
+
relative_uri = '/1.0/kb/accounts/abc-1234'
|
161
|
+
options = {}
|
162
|
+
uri = http_adapter.send(:build_uri, relative_uri, options)
|
163
|
+
expect(uri.to_s).to eq('http://example.com:8080/1.0/kb/accounts/abc-1234')
|
164
|
+
end
|
165
|
+
|
166
|
+
it 'should handle already encoded URI with query string without double encoding' do
|
167
|
+
relative_uri = '/1.0/kb/accounts/my%20account?search=test%20value'
|
168
|
+
options = {}
|
169
|
+
uri = http_adapter.send(:build_uri, relative_uri, options)
|
170
|
+
expect(uri.to_s).to eq('http://example.com:8080/1.0/kb/accounts/my%20account?search=test%20value')
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
context 'with query parameters in options' do
|
175
|
+
it 'should add simple query parameters' do
|
176
|
+
relative_uri = '/1.0/kb/accounts'
|
177
|
+
options = { params: { accountId: '123', limit: 10 } }
|
178
|
+
uri = http_adapter.send(:build_uri, relative_uri, options)
|
179
|
+
expect(uri.query).to include('accountId=123')
|
180
|
+
expect(uri.query).to include('limit=10')
|
181
|
+
end
|
182
|
+
|
183
|
+
it 'should handle array parameters' do
|
184
|
+
relative_uri = '/1.0/kb/accounts'
|
185
|
+
options = { params: { tags: ['premium', 'business'] } }
|
186
|
+
uri = http_adapter.send(:build_uri, relative_uri, options)
|
187
|
+
expect(uri.query).to include('tags=premium')
|
188
|
+
expect(uri.query).to include('tags=business')
|
189
|
+
end
|
190
|
+
|
191
|
+
it 'should merge with existing query string in relative URI' do
|
192
|
+
relative_uri = '/1.0/kb/accounts?existing=value'
|
193
|
+
options = { params: { new: 'param' } }
|
194
|
+
uri = http_adapter.send(:build_uri, relative_uri, options)
|
195
|
+
expect(uri.query).to include('existing=value')
|
196
|
+
expect(uri.query).to include('new=param')
|
197
|
+
end
|
198
|
+
|
199
|
+
it 'should handle empty params hash' do
|
200
|
+
relative_uri = '/1.0/kb/accounts'
|
201
|
+
options = { params: {} }
|
202
|
+
uri = http_adapter.send(:build_uri, relative_uri, options)
|
203
|
+
expect(uri.query).to be_nil
|
204
|
+
end
|
205
|
+
|
206
|
+
it 'should encode special characters in query parameters' do
|
207
|
+
relative_uri = '/1.0/kb/accounts'
|
208
|
+
options = { params: { search: 'test & special chars' } }
|
209
|
+
uri = http_adapter.send(:build_uri, relative_uri, options)
|
210
|
+
expect(uri.query).to include('search=test+%26+special+chars')
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
context 'with plugin properties' do
|
215
|
+
it 'should handle pluginProperty option' do
|
216
|
+
contract_property = KillBillClient::Model::PluginPropertyAttributes.new
|
217
|
+
contract_property.key = :contractId
|
218
|
+
contract_property.value = 'test-123'
|
219
|
+
relative_uri = '/1.0/kb/accounts'
|
220
|
+
options = {
|
221
|
+
params: {},
|
222
|
+
pluginProperty: [contract_property]
|
223
|
+
}
|
224
|
+
uri = http_adapter.send(:build_uri, relative_uri, options)
|
225
|
+
expect(uri.query).to include('pluginProperty=contractId%3Dtest-123')
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
context 'with control plugin names' do
|
230
|
+
it 'should handle controlPluginNames option' do
|
231
|
+
relative_uri = '/1.0/kb/accounts'
|
232
|
+
options = {
|
233
|
+
params: {},
|
234
|
+
controlPluginNames: ['plugin1', 'plugin2']
|
235
|
+
}
|
236
|
+
uri = http_adapter.send(:build_uri, relative_uri, options)
|
237
|
+
expect(uri.query).to include('controlPluginName=plugin1')
|
238
|
+
expect(uri.query).to include('controlPluginName=plugin2')
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
context 'with spaces in path segments' do
|
243
|
+
it 'should encode spaces in relative URI path segments' do
|
244
|
+
relative_uri = '/1.0/kb/accounts/search/Kill Bill Client'
|
245
|
+
options = {}
|
246
|
+
uri = http_adapter.send(:build_uri, relative_uri, options)
|
247
|
+
expect(uri.to_s).to eq('http://example.com:8080/1.0/kb/accounts/search/Kill%20Bill%20Client')
|
248
|
+
end
|
249
|
+
|
250
|
+
it 'should handle absolute URI with spaces in path' do
|
251
|
+
relative_uri = 'http://127.0.0.1:8080/1.0/kb/accounts/search/Kill Bill Client'
|
252
|
+
options = {}
|
253
|
+
uri = http_adapter.send(:build_uri, relative_uri, options)
|
254
|
+
expect(uri.to_s).to eq('http://127.0.0.1:8080/1.0/kb/accounts/search/Kill%20Bill%20Client')
|
255
|
+
end
|
256
|
+
|
257
|
+
it 'should preserve scheme and host when encoding spaces in absolute URI' do
|
258
|
+
relative_uri = 'https://api.example.com:9090/search/My Test Account'
|
259
|
+
options = {}
|
260
|
+
uri = http_adapter.send(:build_uri, relative_uri, options)
|
261
|
+
expect(uri.to_s).to eq('https://api.example.com:9090/search/My%20Test%20Account')
|
262
|
+
expect(uri.scheme).to eq('https')
|
263
|
+
expect(uri.host).to eq('api.example.com')
|
264
|
+
expect(uri.port).to eq(9090)
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
context 'with nil values in query parameters' do
|
269
|
+
it 'should skip nil values in params' do
|
270
|
+
relative_uri = '/1.0/kb/accounts'
|
271
|
+
options = {
|
272
|
+
params: {
|
273
|
+
account: nil,
|
274
|
+
withStackTrace: true,
|
275
|
+
limit: 10
|
276
|
+
}
|
277
|
+
}
|
278
|
+
uri = http_adapter.send(:build_uri, relative_uri, options)
|
279
|
+
expect(uri.query).not_to include('account=')
|
280
|
+
expect(uri.query).to include('withStackTrace=true')
|
281
|
+
expect(uri.query).to include('limit=10')
|
282
|
+
end
|
283
|
+
|
284
|
+
it 'should handle all nil values in params' do
|
285
|
+
relative_uri = '/1.0/kb/accounts'
|
286
|
+
options = {
|
287
|
+
params: {
|
288
|
+
account: nil,
|
289
|
+
tenant: nil
|
290
|
+
}
|
291
|
+
}
|
292
|
+
uri = http_adapter.send(:build_uri, relative_uri, options)
|
293
|
+
expect(uri.query).to be_nil
|
294
|
+
end
|
295
|
+
|
296
|
+
it 'should handle mixed nil and valid values' do
|
297
|
+
relative_uri = '/1.0/kb/accounts'
|
298
|
+
options = {
|
299
|
+
params: {
|
300
|
+
account: nil,
|
301
|
+
withStackTrace: true,
|
302
|
+
offset: 0,
|
303
|
+
search: nil,
|
304
|
+
limit: 50
|
305
|
+
}
|
306
|
+
}
|
307
|
+
uri = http_adapter.send(:build_uri, relative_uri, options)
|
308
|
+
expect(uri.query).not_to include('account=')
|
309
|
+
expect(uri.query).not_to include('search=')
|
310
|
+
expect(uri.query).to include('withStackTrace=true')
|
311
|
+
expect(uri.query).to include('offset=0')
|
312
|
+
expect(uri.query).to include('limit=50')
|
313
|
+
end
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
describe '#encode_params' do
|
318
|
+
let(:http_adapter) { DummyForHTTPAdapter.new }
|
319
|
+
|
320
|
+
it 'should filter out nil values from params' do
|
321
|
+
options = {
|
322
|
+
params: {
|
323
|
+
account: nil,
|
324
|
+
withStackTrace: true,
|
325
|
+
limit: 10,
|
326
|
+
search: nil
|
327
|
+
}
|
328
|
+
}
|
329
|
+
query_string = http_adapter.send(:encode_params, options)
|
330
|
+
expect(query_string).to include('withStackTrace=true')
|
331
|
+
expect(query_string).to include('limit=10')
|
332
|
+
expect(query_string).not_to include('account=')
|
333
|
+
expect(query_string).not_to include('search=')
|
334
|
+
end
|
335
|
+
|
336
|
+
it 'should return nil when all params are nil' do
|
337
|
+
options = {
|
338
|
+
params: {
|
339
|
+
account: nil,
|
340
|
+
tenant: nil
|
341
|
+
}
|
342
|
+
}
|
343
|
+
query_string = http_adapter.send(:encode_params, options)
|
344
|
+
expect(query_string).to be_nil
|
345
|
+
end
|
346
|
+
|
347
|
+
it 'should return nil when params hash is empty' do
|
348
|
+
options = { params: {} }
|
349
|
+
query_string = http_adapter.send(:encode_params, options)
|
350
|
+
expect(query_string).to be_nil
|
351
|
+
end
|
352
|
+
|
353
|
+
it 'should handle options without params key' do
|
354
|
+
options = { account: nil, withStackTrace: true }
|
355
|
+
query_string = http_adapter.send(:encode_params, options)
|
356
|
+
expect(query_string).to be_nil
|
357
|
+
end
|
358
|
+
end
|
109
359
|
end
|
110
360
|
|
111
361
|
class DummyForHTTPAdapter
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: killbill-client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.0.
|
4
|
+
version: 4.0.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Killbill core team
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-
|
11
|
+
date: 2025-07-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: gem-release
|