aitch 1.1.0 → 1.2.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/.github/FUNDING.yml +3 -0
- data/.github/workflows/tests.yml +50 -0
- data/.rubocop.yml +1 -1
- data/README.md +43 -14
- data/aitch.gemspec +1 -0
- data/lib/aitch/configuration.rb +4 -0
- data/lib/aitch/ext/to_query.rb +0 -6
- data/lib/aitch/location.rb +1 -1
- data/lib/aitch/namespace.rb +3 -5
- data/lib/aitch/request.rb +11 -8
- data/lib/aitch/response.rb +1 -1
- data/lib/aitch/uri.rb +3 -3
- data/lib/aitch/version.rb +1 -1
- data/test/aitch/request_test.rb +13 -0
- data/test/aitch/response/custom_response_parser_test.rb +0 -4
- data/test/aitch/response/default_response_parser_test.rb +14 -0
- data/test/aitch/to_query_test.rb +28 -0
- metadata +13 -8
- data/.travis.yml +0 -20
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a3f4cd3269e5b709f85e34148b2ed6aad4a9080f9903dc625d4840d8ffc2293a
|
4
|
+
data.tar.gz: d97a344ec31e5f6ba528f1d359b96aa2860ab51e1cdde507d5cbc0557e9a75a4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a11acc6e165677b372cd4c9a07b9fd33640611a056fb1c437917ffa1d89ab0520bcaf9943cd5e8f72a3f088942a8f7b9d968a59ec7207dc6a061f54364216aba
|
7
|
+
data.tar.gz: a0228f9d4719ee0fddd2932fece69c454062d40f4526612b41044168198f7ec91478267163e343a995ceb5f8bbcdc1d4d228d8edb73a9845b5aa0d08145581c9
|
data/.github/FUNDING.yml
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
---
|
2
|
+
name: Tests
|
3
|
+
|
4
|
+
on:
|
5
|
+
pull_request:
|
6
|
+
branches:
|
7
|
+
- main
|
8
|
+
push:
|
9
|
+
branches:
|
10
|
+
- main
|
11
|
+
jobs:
|
12
|
+
build:
|
13
|
+
name: Tests with Ruby ${{ matrix.ruby }}
|
14
|
+
runs-on: "ubuntu-latest"
|
15
|
+
strategy:
|
16
|
+
fail-fast: false
|
17
|
+
matrix:
|
18
|
+
ruby: ["2.7.x", "2.6.x", "2.5.x", "3.0.x", "3.1.x"]
|
19
|
+
|
20
|
+
steps:
|
21
|
+
- uses: actions/checkout@v1
|
22
|
+
|
23
|
+
- uses: actions/cache@v2
|
24
|
+
with:
|
25
|
+
path: vendor/bundle
|
26
|
+
key: >
|
27
|
+
${{ runner.os }}-${{ matrix.ruby }}-gems-${{
|
28
|
+
hashFiles('aitch.gemspec') }}
|
29
|
+
restore-keys: >
|
30
|
+
${{ runner.os }}-${{ matrix.ruby }}-gems-${{
|
31
|
+
hashFiles('aitch.gemspec') }}
|
32
|
+
|
33
|
+
- name: Set up Ruby
|
34
|
+
uses: actions/setup-ruby@v1
|
35
|
+
with:
|
36
|
+
ruby-version: ${{ matrix.ruby }}
|
37
|
+
|
38
|
+
- name: Install gem dependencies
|
39
|
+
env:
|
40
|
+
RAILS_ENV: test
|
41
|
+
run: |
|
42
|
+
gem install bundler
|
43
|
+
bundle config path vendor/bundle
|
44
|
+
bundle update --jobs 4 --retry 3
|
45
|
+
|
46
|
+
- name: Run Tests
|
47
|
+
env:
|
48
|
+
RAILS_ENV: test
|
49
|
+
run: |
|
50
|
+
bundle exec rake
|
data/.rubocop.yml
CHANGED
data/README.md
CHANGED
@@ -1,24 +1,24 @@
|
|
1
1
|
# Aitch
|
2
2
|
|
3
|
-
[](https://github.com/fnando/aitch)
|
4
4
|
[](https://codeclimate.com/github/fnando/aitch)
|
5
|
-
[](https://rubygems.org/gems/aitch)
|
6
|
+
[](https://rubygems.org/gems/aitch)
|
7
7
|
|
8
8
|
A simple HTTP client.
|
9
9
|
|
10
10
|
Features:
|
11
11
|
|
12
|
-
|
13
|
-
|
14
|
-
|
12
|
+
- Supports Gzip|Deflate response
|
13
|
+
- Automatically parses JSON, HTML and XML responses
|
14
|
+
- Automatically follows redirect
|
15
15
|
|
16
16
|
## Installation
|
17
17
|
|
18
18
|
Add this line to your application's Gemfile:
|
19
19
|
|
20
20
|
```ruby
|
21
|
-
gem
|
21
|
+
gem "aitch"
|
22
22
|
```
|
23
23
|
|
24
24
|
And then execute:
|
@@ -54,6 +54,9 @@ Aitch.configure do |config|
|
|
54
54
|
|
55
55
|
# Set the logger.
|
56
56
|
config.logger = nil
|
57
|
+
|
58
|
+
# Set the base url.
|
59
|
+
config.base_url = nil
|
57
60
|
end
|
58
61
|
```
|
59
62
|
|
@@ -115,7 +118,8 @@ response.data # Parsed response body
|
|
115
118
|
|
116
119
|
#### Parsing JSON, XML and HTML with Nokogiri
|
117
120
|
|
118
|
-
If your response is a JSON, XML or a HTML content type, we'll automatically
|
121
|
+
If your response is a JSON, XML or a HTML content type, we'll automatically
|
122
|
+
convert the response into the appropriate object.
|
119
123
|
|
120
124
|
```ruby
|
121
125
|
response = Aitch.get("http://simplesideias.com.br")
|
@@ -159,7 +163,8 @@ The request:
|
|
159
163
|
Aitch.get("http://example.org")
|
160
164
|
```
|
161
165
|
|
162
|
-
If the redirect limit is exceeded, then the `Aitch::TooManyRedirectsError`
|
166
|
+
If the redirect limit is exceeded, then the `Aitch::TooManyRedirectsError`
|
167
|
+
exception is raised.
|
163
168
|
|
164
169
|
### Basic auth
|
165
170
|
|
@@ -207,7 +212,8 @@ Request.get("http://example.org")
|
|
207
212
|
|
208
213
|
### Validating responses
|
209
214
|
|
210
|
-
When you know the kind of response you're expecting, you can validate it by
|
215
|
+
When you know the kind of response you're expecting, you can validate it by
|
216
|
+
specifying the `expect` option.
|
211
217
|
|
212
218
|
```ruby
|
213
219
|
Aitch.get do
|
@@ -216,7 +222,8 @@ Aitch.get do
|
|
216
222
|
end
|
217
223
|
```
|
218
224
|
|
219
|
-
If this request receives anything other than `200`, it will raise a
|
225
|
+
If this request receives anything other than `200`, it will raise a
|
226
|
+
`Aitch::StatusCodeError` exception.
|
220
227
|
|
221
228
|
```
|
222
229
|
Expect(200 OK) <=> Actual(404 Not Found)
|
@@ -226,7 +233,10 @@ You can also provide a list of accepted statuses, like `expect: [200, 201]`.
|
|
226
233
|
|
227
234
|
### Response Parsers
|
228
235
|
|
229
|
-
You can register new response parsers by using
|
236
|
+
You can register new response parsers by using
|
237
|
+
`Aitch::ResponseParser.register(name, parser)`, where parser must implement the
|
238
|
+
methods `match?(content_type)` and `load(response_body)`. This is how you could
|
239
|
+
load CSV values.
|
230
240
|
|
231
241
|
```ruby
|
232
242
|
require "csv"
|
@@ -248,7 +258,8 @@ end
|
|
248
258
|
Aitch::ResponseParser.prepend(:csv, CSVParser)
|
249
259
|
```
|
250
260
|
|
251
|
-
The default behavior is returning the response body. You can replace it as the
|
261
|
+
The default behavior is returning the response body. You can replace it as the
|
262
|
+
following:
|
252
263
|
|
253
264
|
```ruby
|
254
265
|
module DefaultParser
|
@@ -272,13 +283,31 @@ Aitch::ResponseParser.append(:default, DefaultParser)
|
|
272
283
|
|
273
284
|
Aitch comes with response parsers for HTML, XML and JSON.
|
274
285
|
|
275
|
-
By default, the JSON parser will be `JSON`. To set it to something else, use
|
286
|
+
By default, the JSON parser will be `JSON`. To set it to something else, use
|
287
|
+
`Aitch::ResponseParser::JSONParser.engine`.
|
276
288
|
|
277
289
|
```ruby
|
278
290
|
require "oj"
|
279
291
|
Aitch::ResponseParser::JSONParser.engine = Oj
|
280
292
|
```
|
281
293
|
|
294
|
+
### Setting the base url
|
295
|
+
|
296
|
+
When you're creating a wrapper for an API, usually the hostname is the same for
|
297
|
+
the whole API. In this case, you can avoid having to pass it around all the time
|
298
|
+
by setting `Aitch::Configuration#base_url`. This option is meant to be used when
|
299
|
+
you instantiate a new namespace.
|
300
|
+
|
301
|
+
```ruby
|
302
|
+
Client = Aitch::Namespace.new
|
303
|
+
|
304
|
+
Client.configure do |config|
|
305
|
+
config.base_url = "https://api.example.com"
|
306
|
+
end
|
307
|
+
|
308
|
+
Client.get("/users")
|
309
|
+
```
|
310
|
+
|
282
311
|
## Contributing
|
283
312
|
|
284
313
|
1. Fork it
|
data/aitch.gemspec
CHANGED
@@ -11,6 +11,7 @@ Gem::Specification.new do |spec|
|
|
11
11
|
spec.summary = spec.description
|
12
12
|
spec.homepage = "http://rubygems.org/gems/aitch"
|
13
13
|
spec.license = "MIT"
|
14
|
+
spec.required_ruby_version = Gem::Requirement.new(">= 2.5.0")
|
14
15
|
|
15
16
|
spec.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
|
16
17
|
spec.executables = spec.files.grep(%r{^bin/}) {|f| File.basename(f) }
|
data/lib/aitch/configuration.rb
CHANGED
@@ -23,12 +23,16 @@ module Aitch
|
|
23
23
|
# Set the logger.
|
24
24
|
attr_accessor :logger
|
25
25
|
|
26
|
+
# Set the base url.
|
27
|
+
attr_accessor :base_url
|
28
|
+
|
26
29
|
def initialize
|
27
30
|
@timeout = 10
|
28
31
|
@redirect_limit = 5
|
29
32
|
@follow_redirect = true
|
30
33
|
@user_agent = "Aitch/#{Aitch::VERSION} (http://rubygems.org/gems/aitch)"
|
31
34
|
@default_headers = {}
|
35
|
+
@base_url = nil
|
32
36
|
end
|
33
37
|
|
34
38
|
def to_h
|
data/lib/aitch/ext/to_query.rb
CHANGED
@@ -37,12 +37,6 @@ class FalseClass
|
|
37
37
|
end
|
38
38
|
|
39
39
|
class Array
|
40
|
-
# Calls <tt>to_param</tt> on all its elements and joins the result with
|
41
|
-
# slashes. This is used by <tt>url_for</tt> in Action Pack.
|
42
|
-
def to_param
|
43
|
-
collect(&:to_param).join "/"
|
44
|
-
end
|
45
|
-
|
46
40
|
# Converts an array into a string suitable for use as a URL query string,
|
47
41
|
# using the given +key+ as the param name.
|
48
42
|
#
|
data/lib/aitch/location.rb
CHANGED
@@ -10,7 +10,7 @@ module Aitch
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def location
|
13
|
-
return current_url unless current_url.match(%r{\A/})
|
13
|
+
return current_url unless current_url.match?(%r{\A/}) # rubocop:disable Performance/StartWith
|
14
14
|
|
15
15
|
uri = find_uri_with_host
|
16
16
|
url = ["#{uri.scheme}://#{uri.hostname}"]
|
data/lib/aitch/namespace.rb
CHANGED
@@ -25,7 +25,7 @@ module Aitch
|
|
25
25
|
headers ||= {}
|
26
26
|
options ||= {}
|
27
27
|
|
28
|
-
if
|
28
|
+
if block
|
29
29
|
dsl = DSL.new
|
30
30
|
dsl.instance_eval(&block)
|
31
31
|
args = dsl.to_h
|
@@ -38,10 +38,8 @@ module Aitch
|
|
38
38
|
}
|
39
39
|
end
|
40
40
|
|
41
|
-
args
|
42
|
-
|
43
|
-
options: config.to_h.merge(Utils.symbolize_keys(args[:options]))
|
44
|
-
)
|
41
|
+
args[:request_method] = request_method
|
42
|
+
args[:options] = config.to_h.merge(Utils.symbolize_keys(args[:options]))
|
45
43
|
|
46
44
|
Request.new(args).perform
|
47
45
|
end
|
data/lib/aitch/request.rb
CHANGED
@@ -2,12 +2,7 @@
|
|
2
2
|
|
3
3
|
module Aitch
|
4
4
|
class Request
|
5
|
-
attr_accessor :request_method
|
6
|
-
attr_accessor :url
|
7
|
-
attr_accessor :data
|
8
|
-
attr_accessor :headers
|
9
|
-
attr_accessor :options
|
10
|
-
attr_accessor :redirects
|
5
|
+
attr_accessor :request_method, :url, :data, :headers, :options, :redirects
|
11
6
|
|
12
7
|
alias params= data=
|
13
8
|
alias body= data=
|
@@ -62,7 +57,15 @@ module Aitch
|
|
62
57
|
end
|
63
58
|
|
64
59
|
def uri
|
65
|
-
@uri ||=
|
60
|
+
@uri ||= begin
|
61
|
+
normalized_url = if url.start_with?("/") && options[:base_url]
|
62
|
+
File.join(options[:base_url], url)
|
63
|
+
else
|
64
|
+
url
|
65
|
+
end
|
66
|
+
|
67
|
+
URI.new(normalized_url, data, http_method_class::REQUEST_HAS_BODY)
|
68
|
+
end
|
66
69
|
end
|
67
70
|
|
68
71
|
def http_method_class
|
@@ -76,7 +79,7 @@ module Aitch
|
|
76
79
|
body_data = data
|
77
80
|
body_data = data.to_h if data.respond_to?(:to_h)
|
78
81
|
|
79
|
-
if content_type.to_s
|
82
|
+
if content_type.to_s.match?(/\bjson\b/)
|
80
83
|
body_data = ResponseParser::JSONParser.engine.dump(body_data)
|
81
84
|
end
|
82
85
|
|
data/lib/aitch/response.rb
CHANGED
data/lib/aitch/uri.rb
CHANGED
@@ -6,15 +6,15 @@ module Aitch
|
|
6
6
|
|
7
7
|
def_delegators :@uri, :host, :port, :scheme
|
8
8
|
|
9
|
-
def initialize(url, data = {}, request_has_body = false)
|
9
|
+
def initialize(url, data = {}, request_has_body = false) # rubocop:disable Style/OptionalBooleanParameter
|
10
10
|
@url = url
|
11
11
|
@data = data
|
12
12
|
@request_has_body = request_has_body
|
13
13
|
|
14
14
|
begin
|
15
15
|
@uri = ::URI.parse(url)
|
16
|
-
rescue ::URI::InvalidURIError =>
|
17
|
-
raise InvalidURIError,
|
16
|
+
rescue ::URI::InvalidURIError => error
|
17
|
+
raise InvalidURIError, error
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
data/lib/aitch/version.rb
CHANGED
data/test/aitch/request_test.rb
CHANGED
@@ -159,4 +159,17 @@ class RequestTest < Minitest::Test
|
|
159
159
|
assert_equal "0.1", last_request.headers["Rendering"]
|
160
160
|
assert_equal "user:pass", Base64.decode64(last_request.headers["Authorization"].split(" ").last)
|
161
161
|
end
|
162
|
+
|
163
|
+
test "uses base url" do
|
164
|
+
register_uri(:get, /.+/)
|
165
|
+
|
166
|
+
client = Aitch::Namespace.new
|
167
|
+
client.configure {|c| c.base_url = "https://example.com" }
|
168
|
+
|
169
|
+
client.get("/some/path")
|
170
|
+
|
171
|
+
assert_equal "/some/path", last_request.uri.request_uri
|
172
|
+
assert_equal "example.com", last_request.uri.host
|
173
|
+
assert_equal "https", last_request.uri.scheme
|
174
|
+
end
|
162
175
|
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "test_helper"
|
4
|
+
require "csv"
|
5
|
+
|
6
|
+
class DefaultResponseParserTest < Minitest::Test
|
7
|
+
test "returns application/custom" do
|
8
|
+
register_uri(:get, "http://example.org/file.custom", body: "1,2,3", content_type: "application/custom")
|
9
|
+
response = Aitch.get("http://example.org/file.custom")
|
10
|
+
|
11
|
+
assert_instance_of String, response.data
|
12
|
+
assert_equal %[1,2,3], response.data
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "test_helper"
|
4
|
+
|
5
|
+
class ToQueryTest < Minitest::Test
|
6
|
+
test "converts array" do
|
7
|
+
assert_equal "hobbies%5B%5D=Rails&hobbies%5B%5D=coding",
|
8
|
+
%w[Rails coding].to_query("hobbies")
|
9
|
+
assert_equal "hobbies%5B%5D=",
|
10
|
+
[].to_query("hobbies")
|
11
|
+
end
|
12
|
+
|
13
|
+
test "converts hash" do
|
14
|
+
assert_equal "name=David&nationality=Danish",
|
15
|
+
{name: "David", nationality: "Danish"}.to_query
|
16
|
+
|
17
|
+
assert_equal "user%5Bname%5D=David&user%5Bnationality%5D=Danish",
|
18
|
+
{name: "David", nationality: "Danish"}.to_query("user")
|
19
|
+
end
|
20
|
+
|
21
|
+
test "converts booleans" do
|
22
|
+
assert_equal "done=false&ready=true",
|
23
|
+
{ready: true, done: false}.to_query
|
24
|
+
|
25
|
+
assert_equal "states%5B%5D=true&states%5B%5D=false",
|
26
|
+
[true, false].to_query("states")
|
27
|
+
end
|
28
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: aitch
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nando Vieira
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-07-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: nokogiri
|
@@ -171,9 +171,10 @@ executables: []
|
|
171
171
|
extensions: []
|
172
172
|
extra_rdoc_files: []
|
173
173
|
files:
|
174
|
+
- ".github/FUNDING.yml"
|
175
|
+
- ".github/workflows/tests.yml"
|
174
176
|
- ".gitignore"
|
175
177
|
- ".rubocop.yml"
|
176
|
-
- ".travis.yml"
|
177
178
|
- CHANGELOG.md
|
178
179
|
- Gemfile
|
179
180
|
- LICENSE.txt
|
@@ -214,6 +215,7 @@ files:
|
|
214
215
|
- test/aitch/request/status_code_validation_test.rb
|
215
216
|
- test/aitch/request_test.rb
|
216
217
|
- test/aitch/response/custom_response_parser_test.rb
|
218
|
+
- test/aitch/response/default_response_parser_test.rb
|
217
219
|
- test/aitch/response/errors_test.rb
|
218
220
|
- test/aitch/response/html_response_test.rb
|
219
221
|
- test/aitch/response/json_response_test.rb
|
@@ -226,6 +228,7 @@ files:
|
|
226
228
|
- test/aitch/response_parser/json_parser_test.rb
|
227
229
|
- test/aitch/response_parser/xml_parser_test.rb
|
228
230
|
- test/aitch/response_test.rb
|
231
|
+
- test/aitch/to_query_test.rb
|
229
232
|
- test/aitch/uri_test.rb
|
230
233
|
- test/aitch/utils/symbolize_keys_test.rb
|
231
234
|
- test/aitch/utils/underscore_test.rb
|
@@ -236,7 +239,7 @@ homepage: http://rubygems.org/gems/aitch
|
|
236
239
|
licenses:
|
237
240
|
- MIT
|
238
241
|
metadata: {}
|
239
|
-
post_install_message:
|
242
|
+
post_install_message:
|
240
243
|
rdoc_options: []
|
241
244
|
require_paths:
|
242
245
|
- lib
|
@@ -244,15 +247,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
244
247
|
requirements:
|
245
248
|
- - ">="
|
246
249
|
- !ruby/object:Gem::Version
|
247
|
-
version:
|
250
|
+
version: 2.5.0
|
248
251
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
249
252
|
requirements:
|
250
253
|
- - ">="
|
251
254
|
- !ruby/object:Gem::Version
|
252
255
|
version: '0'
|
253
256
|
requirements: []
|
254
|
-
rubygems_version: 3.
|
255
|
-
signing_key:
|
257
|
+
rubygems_version: 3.3.17
|
258
|
+
signing_key:
|
256
259
|
specification_version: 4
|
257
260
|
summary: A simple HTTP client
|
258
261
|
test_files:
|
@@ -268,6 +271,7 @@ test_files:
|
|
268
271
|
- test/aitch/request/status_code_validation_test.rb
|
269
272
|
- test/aitch/request_test.rb
|
270
273
|
- test/aitch/response/custom_response_parser_test.rb
|
274
|
+
- test/aitch/response/default_response_parser_test.rb
|
271
275
|
- test/aitch/response/errors_test.rb
|
272
276
|
- test/aitch/response/html_response_test.rb
|
273
277
|
- test/aitch/response/json_response_test.rb
|
@@ -280,6 +284,7 @@ test_files:
|
|
280
284
|
- test/aitch/response_parser/json_parser_test.rb
|
281
285
|
- test/aitch/response_parser/xml_parser_test.rb
|
282
286
|
- test/aitch/response_test.rb
|
287
|
+
- test/aitch/to_query_test.rb
|
283
288
|
- test/aitch/uri_test.rb
|
284
289
|
- test/aitch/utils/symbolize_keys_test.rb
|
285
290
|
- test/aitch/utils/underscore_test.rb
|
data/.travis.yml
DELETED
@@ -1,20 +0,0 @@
|
|
1
|
-
---
|
2
|
-
language: ruby
|
3
|
-
script: "bundle exec rake"
|
4
|
-
before_install: gem install bundler
|
5
|
-
cache: bundler
|
6
|
-
sudo: false
|
7
|
-
|
8
|
-
rvm:
|
9
|
-
- "2.7"
|
10
|
-
|
11
|
-
gemfile:
|
12
|
-
- Gemfile
|
13
|
-
|
14
|
-
notifications:
|
15
|
-
email: false
|
16
|
-
|
17
|
-
addons:
|
18
|
-
code_climate:
|
19
|
-
repo_token:
|
20
|
-
secure: "j/AjAG1u7NtdldftKUXnTMKC7CPd2R/O55ejzZnJi5IlHKKKZgJla1Z0YHTsGr75JQS7Q00D0L3TOSY8fLhV6fXO3Gn+9ShKTFRV5+iUtzlV0AWKBw+uENKNJH4jn1FhCoV8Th0I6OKtq98PqZ/hLR2J+/Uv3oCBwyriyJqag2U="
|