httpigeon 2.0.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: e9a75a860ebdb91a4efcfe5d69f7e7a04da7b6f1762b2a67fb1d9ec5aefe2cab
4
+ data.tar.gz: 47f3ac2004f2d716888b36febc71e411ae5afdc8d62bc1a47b536c8fa100a1e8
5
+ SHA512:
6
+ metadata.gz: e8f8f90f1aa68d59f67e4c3fe51932450755c291092ba7e9baa0e8f17502d09c7adc6186c9f1c229a5b54470192dd6b0c0e4d8e4e6c69dc1dab4d051b923dfbc
7
+ data.tar.gz: dfd927e24af3dfc5c08d9c7e6a7a9767e5f9207b87286143b580c33f36525f24f87a9656dc133b84807f84001f9381ab380ed4dc6286444b40c1ab5f664ff59d
@@ -0,0 +1,7 @@
1
+ [Issue](link/to/ticket)
2
+
3
+ #### Description
4
+
5
+ #### Testing Steps
6
+
7
+ #### Notes
@@ -0,0 +1,29 @@
1
+ name: CI
2
+
3
+ on:
4
+ pull_request:
5
+ types: [opened, reopened, edited, synchronize]
6
+
7
+ jobs:
8
+ validate-conventional-commit:
9
+ runs-on: ubuntu-latest
10
+ steps:
11
+ - name: Check for conventional commit
12
+ uses: agenthunt/conventional-commit-checker-action@v1.0.0
13
+ with:
14
+ pr-title-regex: '^((build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test)(\(.*\))?(!)?(: (.*\s*)*))|(^Merge (.*\s*)*)|(^Initial commit$)'
15
+ pr-body-regex: '.*'
16
+
17
+ unit-test:
18
+ runs-on: ubuntu-latest
19
+ if: ${{ github.event.action != 'edited' }}
20
+ steps:
21
+ - uses: actions/checkout@v3
22
+ - name: Set up Ruby
23
+ uses: ruby/setup-ruby@v1
24
+ with:
25
+ ruby-version: 3.1
26
+ - name: Install dependencies
27
+ run: bundle install
28
+ - name: Run tests
29
+ run: bundle exec rake spec
@@ -0,0 +1,45 @@
1
+ name: Publish
2
+ on:
3
+ workflow_call:
4
+ inputs:
5
+ public_publish:
6
+ required: false
7
+ type: string
8
+ default: 'false'
9
+ private_publish:
10
+ required: false
11
+ type: string
12
+ default: 'false'
13
+ jobs:
14
+ release:
15
+ name: Release gem
16
+ runs-on: ubuntu-latest
17
+ steps:
18
+ - name: Check out code
19
+ uses: actions/checkout@v3
20
+ - name: Setup Ruby
21
+ uses: ruby/setup-ruby@v1
22
+ with:
23
+ ruby-version: 3.1
24
+ - name: Publish to GitHub
25
+ if: inputs.private_publish == 'true'
26
+ run: |
27
+ mkdir -p $HOME/.gem
28
+ touch $HOME/.gem/credentials
29
+ chmod 0600 $HOME/.gem/credentials
30
+ printf -- "---\n:github: Bearer ${GITHUB_TOKEN}\n" > $HOME/.gem/credentials
31
+ gem build *.gemspec
32
+ gem push --KEY github --host https://rubygems.pkg.github.com/dailypay *.gem
33
+ env:
34
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
35
+ - name: Publish to RubyGems
36
+ if: inputs.public_publish == 'true'
37
+ run: |
38
+ mkdir -p $HOME/.gem
39
+ touch $HOME/.gem/credentials
40
+ chmod 0600 $HOME/.gem/credentials
41
+ printf -- "---\n:rubygems_api_key: ${RUBYGEMS_API_KEY}\n" > $HOME/.gem/credentials
42
+ gem build *.gemspec
43
+ gem push *.gem
44
+ env:
45
+ RUBYGEMS_API_KEY: ${{ secrets.RUBYGEMS_API_KEY }}
@@ -0,0 +1,26 @@
1
+ name: release-please
2
+ on: [pull_request]
3
+
4
+ jobs:
5
+ release-please:
6
+ outputs:
7
+ release_created: ${{ steps.release.outputs.release_created }}
8
+ tag_name: ${{ steps.release.outputs.tag_name }}
9
+ permissions: write-all
10
+ runs-on: ubuntu-latest
11
+ steps:
12
+ - uses: google-github-actions/release-please-action@v3
13
+ id: release
14
+ with:
15
+ release-type: ruby
16
+ package-name: httpigeon
17
+ bump-minor-pre-major: true
18
+ bump-patch-for-minor-pre-major: true
19
+ publish-gem:
20
+ uses: ./.github/workflows/publish.yaml
21
+ needs: [ release-please ]
22
+ # if: needs.release-please.outputs.release_created
23
+ with:
24
+ public_publish: 'true'
25
+ private_publish: 'false'
26
+ secrets: inherit # implicitly forward secrets to called workflow
@@ -0,0 +1,19 @@
1
+ name: Reviewdog
2
+ on: [pull_request]
3
+ jobs:
4
+ rubocop:
5
+ name: rubocop
6
+ runs-on: ubuntu-latest
7
+ steps:
8
+ - name: Check out code
9
+ uses: actions/checkout@v3
10
+ - uses: ruby/setup-ruby@v1
11
+ with:
12
+ ruby-version: 3.1
13
+ - name: rubocop
14
+ uses: reviewdog/action-rubocop@v2
15
+ with:
16
+ rubocop_version: gemfile
17
+ rubocop_extensions: rubocop-rspec:gemfile
18
+ reporter: github-pr-check
19
+ fail_on_error: true
data/.gitignore ADDED
@@ -0,0 +1,19 @@
1
+ ## GENERAL
2
+ /coverage/
3
+ /doc/
4
+ /pkg/
5
+ /spec/reports/
6
+ /tmp/
7
+ .DS_Store
8
+
9
+ ## BUNDLER
10
+ *.gem
11
+ /.bundle/
12
+ Gemfile.lock
13
+ vendor/bundle
14
+
15
+ ## IDEs
16
+ .idea/
17
+ .yardoc
18
+ /.yardoc
19
+ /_yardoc/
data/.rubocop.yml ADDED
@@ -0,0 +1,187 @@
1
+ require:
2
+ - rubocop-rspec
3
+
4
+ AllCops:
5
+ TargetRubyVersion: 3.1.2
6
+ Exclude:
7
+ - 'bin/**/*'
8
+ - 'vendor/**/*'
9
+ NewCops: enable
10
+
11
+ #### ::STYLE ####
12
+
13
+ # Let Ruby do it's thing when the time comes
14
+ Style/FrozenStringLiteralComment:
15
+ Enabled: false
16
+
17
+ # Decision whether to use alias or alias_method is not stylistic
18
+ # See: https://blog.bigbinary.com/2012/01/08/alias-vs-alias-method.html
19
+ Style/Alias:
20
+ Enabled: false
21
+
22
+ # This still common in Rails and usually doesn't result in problems.
23
+ Style/ClassAndModuleChildren:
24
+ Enabled: false
25
+
26
+ Style/CollectionMethods:
27
+ Description: Preferred collection methods.
28
+ StyleGuide: https://github.com/bbatsov/ruby-style-guide#map-find-select-reduce-size
29
+ Enabled: true
30
+ PreferredMethods:
31
+ collect: map
32
+ collect!: map!
33
+ find: detect
34
+ find_all: select
35
+ reduce: inject
36
+
37
+ # We don't enforce per-class documentation.
38
+ Style/Documentation:
39
+ Enabled: false
40
+
41
+ # We don't mind two-line empty methods as they're easier to start editing and
42
+ # pretty common in auto-generated Rails controllers.
43
+ Style/EmptyMethod:
44
+ Enabled: false
45
+
46
+ # We allow hash rockets in rake task dependencies, e.g. task :my_task => :dep.
47
+ Style/HashSyntax:
48
+ EnforcedShorthandSyntax: never
49
+
50
+ # There's no statistical difference between single and double quotes
51
+ # performance.
52
+ # See: https://www.viget.com/articles/just-use-double-quoted-ruby-strings/
53
+ Style/StringLiterals:
54
+ Enabled: false
55
+
56
+ # Ditto for above.
57
+ Style/StringLiteralsInInterpolation:
58
+ Enabled: false
59
+
60
+ Style/BlockDelimiters:
61
+ AllowBracesOnProceduralOneLiners: true
62
+
63
+ Style/ModuleFunction:
64
+ EnforcedStyle: extend_self
65
+
66
+ Style/SymbolArray:
67
+ MinSize: 3
68
+
69
+ Style/WordArray:
70
+ MinSize: 3
71
+
72
+ Style/FetchEnvVar:
73
+ Enabled: false
74
+
75
+ Style/OpenStructUse:
76
+ Enabled: false
77
+
78
+ Style/RedundantInitialize:
79
+ Exclude:
80
+ - 'test/**/*.rb'
81
+
82
+ Style/Semicolon:
83
+ Exclude:
84
+ - 'test/**/*.rb'
85
+
86
+ Style/Lambda:
87
+ Enabled: false
88
+
89
+ #### ::LAYOUT ####
90
+
91
+ Layout/DotPosition:
92
+ Enabled: true
93
+ EnforcedStyle: trailing
94
+
95
+ Layout/MultilineMethodCallIndentation:
96
+ EnforcedStyle: indented_relative_to_receiver
97
+ Enabled: false
98
+ IndentationWidth: ~
99
+
100
+ # This rule does not detect string interpolations reliably,
101
+ # e.g. accuses 'full_messages.join(", ")'
102
+ Layout/SpaceInsideStringInterpolation:
103
+ Enabled: false
104
+
105
+ # Allow long lines with comments
106
+ Layout/LineLength:
107
+ Max: 200
108
+ AllowedPatterns: ['(\A|\s)#']
109
+
110
+ #### ::LINT ####
111
+ Lint/AssignmentInCondition:
112
+ AllowSafeAssignment: false
113
+
114
+ Lint/ConstantDefinitionInBlock:
115
+ Exclude:
116
+ - 'test/**/*.rb'
117
+
118
+ #### ::METRICS ####
119
+
120
+ # Methods should be easy to read, enforcing an arbitrary metric as number
121
+ # of lines is not the way to do it though.
122
+ Metrics/MethodLength:
123
+ Enabled: false
124
+
125
+ Metrics/ClassLength:
126
+ Enabled: false
127
+
128
+ Metrics/AbcSize:
129
+ Enabled: false
130
+
131
+ Metrics/BlockLength:
132
+ Enabled: false
133
+
134
+ Metrics/CyclomaticComplexity:
135
+ Enabled: false
136
+
137
+ Metrics/PerceivedComplexity:
138
+ Enabled: false
139
+
140
+ Metrics/ParameterLists:
141
+ Enabled: false
142
+
143
+ #### ::NAMING ####
144
+
145
+ # Allow arbitrary symbol names
146
+ Naming/VariableNumber:
147
+ CheckSymbols: false
148
+
149
+ Naming/BlockForwarding:
150
+ Enabled: false
151
+
152
+ #### ::GEM ####
153
+ Gemspec/DevelopmentDependencies:
154
+ Enabled: false
155
+
156
+ Gemspec/OrderedDependencies:
157
+ Enabled: false
158
+
159
+ Gemspec/RequireMFA:
160
+ Enabled: false
161
+
162
+ #### ::RSPEC ####
163
+ RSpec/ExampleLength:
164
+ Enabled: false
165
+
166
+ RSpec/MultipleExpectations:
167
+ Max: 10
168
+
169
+ RSpec/AnyInstance:
170
+ Enabled: false
171
+
172
+ RSpec/VerifiedDoubles:
173
+ Enabled: false
174
+
175
+ RSpec/MultipleMemoizedHelpers:
176
+ Max: 10
177
+
178
+ RSpec/NestedGroups:
179
+ Max: 5
180
+
181
+ RSpec/FilePath:
182
+ Exclude:
183
+ - spec/httpigeon/**/*.rb
184
+
185
+ RSpec/SpecFilePathFormat:
186
+ Exclude:
187
+ - spec/httpigeon/**/*.rb
data/CHANGELOG.md ADDED
@@ -0,0 +1,48 @@
1
+ # Changelog
2
+
3
+ ## [2.0.0](https://github.com/dailypay/httpigeon/compare/v1.3.0...v2.0.0) (2023-10-25)
4
+
5
+
6
+ ### ⚠ BREAKING CHANGES
7
+
8
+ * **logging:** Improve redaction mechanism ([#23](https://github.com/dailypay/httpigeon/issues/23))
9
+
10
+ ### Features
11
+
12
+ * Generate request ID header by default ([#25](https://github.com/dailypay/httpigeon/issues/25)) ([c2aa078](https://github.com/dailypay/httpigeon/commit/c2aa078947c422f544ff1b36d77576a2a3681d08))
13
+ * **logging:** Improve redaction mechanism ([#23](https://github.com/dailypay/httpigeon/issues/23)) ([ce090bd](https://github.com/dailypay/httpigeon/commit/ce090bd0124ef3f3ec616d7c0af5a4652be11b0a))
14
+
15
+ ## [1.3.0](https://github.com/dailypay/httpigeon/compare/v1.2.1...v1.3.0) (2023-08-24)
16
+
17
+
18
+ ### Features
19
+
20
+ * **request:** Add helper :get and :post class methods ([#21](https://github.com/dailypay/httpigeon/issues/21)) ([e7427a6](https://github.com/dailypay/httpigeon/commit/e7427a6f1fe2d39e4cce2ec3ea1188e03b563287))
21
+
22
+ ## [1.2.1](https://github.com/dailypay/httpigeon/compare/v1.2.0...v1.2.1) (2023-07-28)
23
+
24
+
25
+ ### Bug Fixes
26
+
27
+ * **logger:** Fix reference to ruby Logger constant ([#19](https://github.com/dailypay/httpigeon/issues/19)) ([ec99bd5](https://github.com/dailypay/httpigeon/commit/ec99bd5b6371256ded6c88c8413b0bd2c926a7a1))
28
+
29
+ ## [1.2.0](https://github.com/dailypay/httpigeon/compare/v1.1.1...v1.2.0) (2023-07-28)
30
+
31
+
32
+ ### Features
33
+
34
+ * Add customizable log redactor that can handle both Hash and String payload. Also add support for auto-generating request IDs ([#14](https://github.com/dailypay/httpigeon/issues/14)) ([c3efa0a](https://github.com/dailypay/httpigeon/commit/c3efa0a510cda687f6a6822e17c1c9600ba4dfd0))
35
+
36
+ ## [1.1.1](https://github.com/dailypay/httpigeon/compare/v1.1.0...v1.1.1) (2023-06-20)
37
+
38
+
39
+ ### Bug Fixes
40
+
41
+ * **httpigeon:** Leave event logger constructor signature up to call site ([#10](https://github.com/dailypay/httpigeon/issues/10)) ([03ba441](https://github.com/dailypay/httpigeon/commit/03ba441c66d8ea6562f218b41cc8f724bd98a4a9))
42
+
43
+ ## 1.0.0 (2023-06-20)
44
+ Initial release
45
+
46
+ ### Features
47
+
48
+ * **httpigeon:** [XAPI-1353] Gemify HTTPigeon library ([#1](https://github.com/dailypay/httpigeon/issues/1)) ([ee89810](https://github.com/dailypay/httpigeon/commit/ee898102b2dffe6623e57a0d799a8b9a37d068a1))
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ # Specify your gem's dependencies in httpigeon-ruby.gemspec
6
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 DailyPay
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,191 @@
1
+ # HTTPigeon
2
+
3
+ As early as 2000 years ago, and as late as 20 years ago, messenger pigeons (a.k.a homing pigeons) were an established and reliable means of long distance communication. This library is dedicated to messenger pigeons and all their contributions towards ancient and modern civilization ❤️.
4
+
5
+ The goal of this library is to add a layer of abstraction on top of [Faraday](https://github.com/lostisland/faraday) client with a much simpler, but also customizable interface, so that making and logging API requests is much easier.
6
+
7
+ ## Usage
8
+
9
+ **Configuration:**
10
+ ```ruby
11
+ HTTPigeon.configure do |config|
12
+ config.default_event_type = # Set a default event type for all requests, overridable per request. Default: 'http.outbound'
13
+ config.default_filter_keys = # Set a default list of keys to be redacted for Hash payloads, overridable per request. Default: []
14
+ config.redactor_string = # Set a string that should be used as the replacement when redacting sensitive data. Default: '[FILTERED]'
15
+ config.log_redactor = # Specify an object to be used for redacting data before logging. Must respond to #redact(data<Hash, String>). Default: nil
16
+ config.event_logger = # Specify an object to be used for logging request roundtrip events. Default: $stdout
17
+ config.auto_generate_request_id = # Auto-generate a uuid for each request and store in a 'X-Request-Id' header? Default: true
18
+ config.exception_notifier = # Specify an object to be used for reporting errors. Must respond to #notify_exception(e<Exception>). Must be defined if :notify_all_exceptions is true
19
+ config.notify_all_exceptions = # Do you want these errors to actually get reported/notified? Default: false
20
+ end
21
+ ```
22
+
23
+ **Instantiating with a block:**
24
+ ```ruby
25
+ # @option [String] base_url the base URI (required)
26
+ request = HTTPigeon::Request.new(base_url: 'https://dummyjson.com') do |connection|
27
+ # connection is an instance of Faraday::Connection
28
+ connection.headers['foo'] = 'barzzz'
29
+ connection.options['timeout'] = 15
30
+ ...
31
+ end
32
+
33
+ # @option [Symbol] method the HTTP verb (default: :get)
34
+ # @option [String] path the request path (default: '/')
35
+ # @option [Hash] payload the body (for writes) or query params (for reads) of the request (default: {})
36
+ request.run(path: '/users/1')
37
+ ```
38
+
39
+ **Instantiating with customizable arguments:**
40
+ ```ruby
41
+ # @param base_url [String] the base URI
42
+ # @param options [Hash] the Faraday connection options (default: {})
43
+ # @param headers [Hash] the request headers (default: {})
44
+ # @param adapter [Faraday::Adapter] the Faraday adapter (default: Net::HTTP)
45
+ # @param logger [Logger] for logging request and response (default: HTTPigeon::Logger)
46
+ # @param event_type [String] for filtering/scoping the logs (default: 'http.outbound')
47
+ # @param log_filters [Array<Symbol, String>] specifies keys in URL, headers and body to be redacted before logging.
48
+ # Can define keys for both Hash and String payloads (default: [])
49
+ request = HTTPigeon::Request.new(base_url: 'https://dummyjson.com', headers: { Accept: 'application/json' }, log_filters: [:api_key, 'access_token', '(client_id=)(\w+)'])
50
+ request.run(path: '/users/1')
51
+ ```
52
+
53
+ **Passing a custom logger:**
54
+
55
+ Your custom logger must respond to `#log` and be a registered Faraday Middleware. It can optionally implement `#on_request_start` and `#on_request_finish`, if you wanted to take certain actions right before and right after a request round-trip (e.g capturing latency).
56
+ Note that if you pass a custom logger, you would have to handle redacting sensitive keys even if you pass a `log_filters` list, unless you subclass `HTTPigeon::Logger`.
57
+ ```ruby
58
+ # The default Rails logger is registered/recognized by Faraday
59
+ class CustomLogger < Logger
60
+ # @param [Faraday::Env] env the Faraday environment instance passed from middleware
61
+ # @param [Hash] data additional data passed from middleware. May contain an :error object if the request was unsuccessful (default: {})
62
+ def log(env, data)
63
+ error = data.delete(:error).to_json
64
+
65
+ log_data = data.merge(
66
+ {
67
+ method: env.method,
68
+ headers: env.request_headers,
69
+ body: env.response_body,
70
+ error: error,
71
+ latency: @end_time - @start_time
72
+ }
73
+ )
74
+
75
+ super(:info, log_data.to_json)
76
+ end
77
+
78
+ # optional method
79
+ def on_request_start
80
+ @start_time = Time.current
81
+ end
82
+
83
+ # optional method
84
+ def on_request_finish
85
+ @end_time = Time.current
86
+ end
87
+ end
88
+
89
+ request = HTTPigeon::Request.new(base_url: 'https://dummyjson.com', logger: CustomLogger.new)
90
+ request.run(path: '/users/1')
91
+ ```
92
+
93
+ **Using the default logger:**
94
+
95
+ ***Event Type***
96
+
97
+ The default logger always adds an `:event_type` key to the log payload that can be used as another filtering/grouping mechanism when querying logs. The default value is 'http.outbound'. To set a different value for a specific request, simply pass the key like so:
98
+ ```ruby
99
+ HTTPigeon::Request.new(base_url: 'https://dummyjson.com', event_type: 'custom.event')
100
+ ```
101
+
102
+ ***Log Filters***
103
+
104
+ > [!IMPORTANT]
105
+ > - Log filtering mechanism does partial redaction by default, unless the value is **4 characters or less**. To have a value fully redacted, you have to explicitly append a replacement to the filter, separated by a `::` (e.g `'ssn::[REDACTED]'`).
106
+ > - Hash log filters are case insensitive
107
+ > - Only ignore case regexp flag (`/i`) is currently supported for log filters and is already applied by default
108
+
109
+ Prior to logging, the default logger will always run it's redactor through:
110
+ - The full request **URL**
111
+ - The request and response **headers**
112
+ - the request and response **body**
113
+
114
+ **Examples:**
115
+
116
+ Examples assume you set `:redactor_string` in your initializer to `[REDACTED]`
117
+
118
+ | Filter | Target | Pre-redaction | Post-redaction | Notes |
119
+ | --- | --- | --- | --- | ----- |
120
+ | `"email"` OR `:email` | Hash | `{ "email": "atuny0@sohu.com" }` | `{ "email": "atu...[REDACTED]" }` | Filters will get applied to nested objects as well. There's no limit on depth |
121
+ | `"email::[REDACTED]"` | Hash | `{ "email": "atuny0@sohu.com" }` | `{ "email": "[REDACTED]" }` | Replacement can be whatever you want and is applied as-is |
122
+ | `"/email/"` | Hash | `{ "email": "atuny0@sohu.com" }` | `{ "email": "atuny0@sohu.com" }` | Regex filters will not get applied to hash keys. This is a design decision to prevent bugs |
123
+ | `"/(email=)(.*\\.[a-z]+)(&\|$)/"` | String | `https://dummyjson.com/users?email=atuny0@sohu.com` | `https://dummyjson.com/users?email=atu...[REDACTED]` | Regex filters must be in proper regex format but wrapped in a string. If no replacement is specified, [regex grouping](https://learn.microsoft.com/en-us/dotnet/standard/base-types/grouping-constructs-in-regular-expressions) MUST be used |
124
+ | `"/email=.*\\.[a-z]+(&\|$)/::email=[REDACTED]"` | String | `https://dummyjson.com/users?email=atuny0@sohu.com` | `https://dummyjson.com/users?email=[REDACTED]` | Replacement can be whatever you want and is applied as-is. No need to use regex grouping when explicitly specifying a replacement |
125
+ | `"(email=)(.*\\.[a-z]+)(&\|$)"` OR `"email"` | String | `https://dummyjson.com/users?email=atuny0@sohu.com` | `https://dummyjson.com/users?email=atuny0@sohu.com` | String filters must be defined in proper regex format, otherwise they will be ignored. This is a design descision to prevent bugs |
126
+
127
+ There are some ready-made, tokenized filter patterns available that you can take advantage of for **URLs** and/or **URI encoded requests**:
128
+ - HTTPigeon::FilterPatterns::EMAIL
129
+ - HTTPigeon::FilterPatterns::PASSWORD
130
+ - HTTPigeon::FilterPatterns::USERNAME
131
+ - HTTPigeon::FilterPatterns::CLIENT_ID
132
+ - HTTPigeon::FilterPatterns::CLIENT_SECRET
133
+
134
+ ```ruby
135
+ # Will truncate the value of any header or payload key matching access_token
136
+ # Will replace the value of any header or payload key matching password with [REDACTED]
137
+ # Will truncate the value of any request param URI encoded payload key matching client_id
138
+ # Will replace the value of any request param URI encoded payload key matching password with [REDACTED]
139
+ HTTPigeon::Request.new(base_url: 'https://dummyjson.com', log_filters: %w[access_token password::[REDACTED] /(client_id=)([0-9a-z]+)*/ /password=\w+/::password=[REDACTED]])
140
+ ```
141
+
142
+ **Running a request:**
143
+
144
+ * You can pass a block to further customize a specific request:
145
+ ```ruby
146
+ request = HTTPigeon::Request.new(base_url: 'https://dummyjson.com')
147
+
148
+ # Returns a Hash (parsed JSON) response or a String if the original response was not valid JSON
149
+ request.run(path: '/users/1') { |request| request.headers['X-Request-Signature'] = Base64.encode64("#{method}::#{path}") }
150
+
151
+ # Access the raw Faraday response
152
+ request.response
153
+
154
+ # Quickly get the response status
155
+ request.response_status
156
+
157
+ # Quickly get the raw response body
158
+ request.response_body
159
+ ```
160
+ * There is a convenient :get and :post class method you can use
161
+ ```ruby
162
+ # @param endpoint [String] the URI endpoint you're trying to hit
163
+ # @param query [Hash] the request query params (default: {})
164
+ # @param headers [Hash] the request headers (default: {})
165
+ # @param event_type [String] the event type for logs grouping (default: 'http.outbound')
166
+ # @param log_filters [Array<Symbol, String>] specifies keys in URL, headers and body to be redacted before logging.
167
+ # @return [HTTPigeon::Response] an object with attributes :request [HTTPigeon::Request], :parsed_response [Hash], and :raw_response [Faraday::Response]
168
+ response = HTTPigeon::Request.get(endpoint, query, headers, event_type, log_filters)
169
+
170
+ # @param endpoint [String] the URI endpoint you're trying to hit
171
+ # @param payload [Hash] the request payload/body (default: {})
172
+ # @param headers [Hash] the request headers (default: {})
173
+ # @param event_type [String] the event type for logs grouping (default: 'http.outbound')
174
+ # @param log_filters [Array<Symbol, String>] specifies keys in URL, headers and body to be redacted before logging.
175
+ # @return [HTTPigeon::Response] an object with attributes :request [HTTPigeon::Request], :parsed_response [Hash], and :raw_response [Faraday::Response]
176
+ response = HTTPigeon::Request.post(endpoint, payload, headers, event_type, log_filters)
177
+ ```
178
+
179
+ ## Development
180
+
181
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
182
+
183
+ To install this gem onto your local machine, run `bundle exec rake install:local`.
184
+
185
+ ## Contributing
186
+
187
+ Bug reports and pull requests are welcome on GitHub at https://github.com/dailypay/httpigeon.
188
+
189
+ **Making Pull Requests:**
190
+
191
+ This project uses [release-please](https://github.com/google-github-actions/release-please-action) for automated releases. As such, any pull request that fails the [conventional commits](https://www.conventionalcommits.org/en/v1.0.0-beta.4/#summary) validation will not be merged.
data/Rakefile ADDED
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec) do |task|
7
+ task.pattern = Dir.glob("spec/**/*_spec.rb")
8
+ task.rspec_opts = "--format documentation"
9
+ end
10
+
11
+ require "rubocop/rake_task"
12
+
13
+ RuboCop::RakeTask.new do |task|
14
+ task.requires << 'rubocop-rspec'
15
+ end
16
+
17
+ task default: %i[spec rubocop]
data/bin/console ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "httpigeon"
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ # (If you use this, don't forget to add pry to your Gemfile!)
11
+ # require "pry"
12
+ # Pry.start
13
+
14
+ require "irb"
15
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/httpigeon.gemspec ADDED
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/httpigeon/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "httpigeon"
7
+ spec.version = HTTPigeon::VERSION
8
+ spec.authors = ["2k-joker"]
9
+ spec.email = ["opensource@dailypay.com"]
10
+ spec.licenses = ["MIT"]
11
+
12
+ spec.summary = "Simple, easy way to make and log HTTP requests and responses"
13
+ spec.description = "Client library that simplifies making and logging HTTP requests and responses. This library is built as an abstraction on top of the Faraday ruby client."
14
+ spec.homepage = "https://github.com/dailypay/#{spec.name}"
15
+ spec.required_ruby_version = Gem::Requirement.new("~> 3.1.0")
16
+
17
+ spec.metadata["homepage_uri"] = spec.homepage
18
+ spec.metadata["source_code_uri"] = spec.homepage
19
+
20
+ # Specify which files should be added to the gem when it is released.
21
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
22
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
23
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
24
+ end
25
+
26
+ spec.bindir = "exe"
27
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
28
+ spec.require_paths = ["lib"]
29
+
30
+ spec.add_dependency "faraday", "~> 2.7.6"
31
+ spec.add_dependency "activesupport", "~> 7.0.4"
32
+
33
+ spec.add_development_dependency "rake", "~> 13.0"
34
+ spec.add_development_dependency "rspec", "~> 3.4"
35
+ spec.add_development_dependency "simplecov"
36
+ spec.add_development_dependency "rubocop", "~> 1.21"
37
+ spec.add_development_dependency "rubocop-rspec", "~> 2.24"
38
+ spec.add_development_dependency "pry", "~> 0.13.1"
39
+
40
+ # For more information and examples about making a new gem, check out our
41
+ # guide at: https://bundler.io/guides/creating_gem.html
42
+ end
@@ -0,0 +1,16 @@
1
+ module HTTPigeon
2
+ class Configuration
3
+ attr_accessor :default_event_type, :default_filter_keys, :redactor_string, :log_redactor, :event_logger, :notify_all_exceptions, :exception_notifier, :auto_generate_request_id
4
+
5
+ def initialize
6
+ @default_event_type = 'http.outbound'
7
+ @default_filter_keys = []
8
+ @redactor_string = '[FILTERED]'
9
+ @log_redactor = nil
10
+ @event_logger = nil
11
+ @auto_generate_request_id = true
12
+ @notify_all_exceptions = false
13
+ @exception_notifier = nil
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,96 @@
1
+ require "active_support/core_ext/hash"
2
+
3
+ module HTTPigeon
4
+ class LogRedactor
5
+ class UnsupportedRegexpError < StandardError; end
6
+
7
+ attr_reader :log_filters
8
+
9
+ def initialize(log_filters: nil)
10
+ @log_filters = log_filters.to_a.map(&:to_s)
11
+ end
12
+
13
+ def redact(data)
14
+ case data
15
+ when Array
16
+ data.map { |datum| redact(datum) }
17
+ when String
18
+ redact_string(data)
19
+ when Hash
20
+ redact_hash(data)
21
+ else
22
+ data
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ def redact_value(value)
29
+ length = value.to_s.length
30
+
31
+ return value unless length.positive?
32
+
33
+ case length
34
+ when 1..4
35
+ HTTPigeon.redactor_string
36
+ when 5..16
37
+ "#{value.to_s[0..2]}...#{HTTPigeon.redactor_string}"
38
+ when 17..32
39
+ "#{value.to_s[0..(length / 4)]}...#{HTTPigeon.redactor_string}"
40
+ else
41
+ "#{value.to_s[0..5]}...#{HTTPigeon.redactor_string}...#{value.to_s[-6..]}"
42
+ end
43
+ end
44
+
45
+ def redact_hash(data)
46
+ data.to_h do |k, v|
47
+ filter = hash_filter_for(k)
48
+
49
+ if filter.present?
50
+ replacement = filter.split('::')[1].presence
51
+ v = replacement.present? ? replacement : redact_value(v)
52
+ end
53
+
54
+ if v.is_a?(Hash)
55
+ [k, redact_hash(v)]
56
+ elsif v.is_a?(Array)
57
+ [k, v.map { |val| redact(val) }]
58
+ else
59
+ [k, v]
60
+ end
61
+ end
62
+ end
63
+
64
+ def redact_string(data)
65
+ log_filters.each do |filter|
66
+ pattern, replacement = filter.split('::')
67
+
68
+ next unless pattern.match?(%r{^/.*/([guysim]*)$})
69
+
70
+ data = if replacement.present?
71
+ data.gsub(regex_for(pattern), replacement)
72
+ else
73
+ data.gsub(regex_for(pattern)) do |sub|
74
+ captures = sub.match(regex_for(pattern))&.captures
75
+
76
+ captures.present? ? captures[0] + redact_value(captures[1]) : sub
77
+ end
78
+ end
79
+ end
80
+
81
+ data
82
+ end
83
+
84
+ def hash_filter_for(key)
85
+ log_filters.detect { |k| k.split('::').first.downcase == key.to_s.downcase }
86
+ end
87
+
88
+ def regex_for(pattern)
89
+ regexp_literal = (pattern.match %r{^(/)(.*)(/(i?))$}).to_a[2].to_s
90
+
91
+ raise UnsupportedRegexpError, "The specified regexp is invalid: #{pattern}. NOTE: Only ignore case (/i) is currently supported." if regexp_literal.blank?
92
+
93
+ Regexp.new(regexp_literal, Regexp::IGNORECASE)
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,84 @@
1
+ require "active_support/core_ext/hash"
2
+ require "active_support/core_ext/object/deep_dup"
3
+
4
+ module HTTPigeon
5
+ class Logger
6
+ attr_reader :event_type, :log_redactor, :start_time, :end_time
7
+
8
+ def initialize(event_type: nil, log_filters: nil)
9
+ @event_type = event_type || HTTPigeon.default_event_type
10
+ @log_redactor = HTTPigeon.log_redactor || HTTPigeon::LogRedactor.new(log_filters: HTTPigeon.default_filter_keys | log_filters.to_a)
11
+ end
12
+
13
+ def log(faraday_env, data = {})
14
+ base_log_data = { event_type: event_type }
15
+ log_data = build_log_data(faraday_env, data).merge(base_log_data)
16
+
17
+ HTTPigeon.event_logger.nil? ? log_to_stdout(log_data) : HTTPigeon.event_logger.log(log_data)
18
+ rescue StandardError => e
19
+ HTTPigeon.exception_notifier.notify_exception(e) if HTTPigeon.notify_all_exceptions
20
+ raise e if ['development', 'test'].include?(ENV['RAILS_ENV'].to_s)
21
+ end
22
+
23
+ def on_request_start
24
+ @start_time = Time.current
25
+ end
26
+
27
+ def on_request_finish
28
+ @end_time = Time.current
29
+ end
30
+
31
+ private
32
+
33
+ def build_log_data(env, data)
34
+ log_data = data.deep_dup
35
+ request_id = env.request_headers.transform_keys(&:downcase)['x-request-id']
36
+ request_latency = end_time - start_time if end_time.present? && start_time.present?
37
+
38
+ log_data[:request] = {
39
+ method: env.method,
40
+ url: redact(env.url.to_s),
41
+ headers: redact(env.request_headers),
42
+ body: redact(env.request_body),
43
+ host: env.url.host,
44
+ path: env.url.path
45
+ }
46
+
47
+ log_data[:response] = {
48
+ headers: redact(env.response_headers),
49
+ body: redact(env.response_body),
50
+ status: env.status
51
+ }
52
+
53
+ log_data[:metadata] = {
54
+ latency: request_latency,
55
+ identifier: request_id,
56
+ protocol: env.url.scheme
57
+ }
58
+
59
+ if log_data[:error].present?
60
+ error = log_data.delete(:error)
61
+ log_data[:error] = {
62
+ type: error.class.name,
63
+ message: error.message,
64
+ backtrace: error.backtrace.last(10)
65
+ }
66
+ end
67
+
68
+ log_data
69
+ end
70
+
71
+ def redact(data)
72
+ return {} if data.blank?
73
+
74
+ data = JSON.parse(data) if data.is_a?(String)
75
+ log_redactor.redact(data)
76
+ rescue JSON::ParserError
77
+ log_redactor.redact(data)
78
+ end
79
+
80
+ def log_to_stdout(log_data)
81
+ ::Logger.new($stdout).log(1, log_data.to_json)
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,37 @@
1
+ require "faraday/middleware_registry"
2
+ require "faraday/middleware"
3
+ require "faraday/response"
4
+
5
+ module HTTPigeon
6
+ module Middleware
7
+ class HTTPigeonLogger < Faraday::Middleware
8
+ def initialize(app, logger)
9
+ super(app)
10
+
11
+ @logger = logger
12
+ end
13
+
14
+ def call(env)
15
+ logger.on_request_start if logger.respond_to?(:on_request_start)
16
+
17
+ super
18
+ rescue StandardError => e
19
+ logger.on_request_finish if logger.respond_to?(:on_request_finish)
20
+ logger.log(env, { error: e })
21
+
22
+ raise e
23
+ end
24
+
25
+ def on_complete(env)
26
+ logger.on_request_finish if logger.respond_to?(:on_request_finish)
27
+ logger.log(env)
28
+ end
29
+
30
+ private
31
+
32
+ attr_reader :logger
33
+ end
34
+ end
35
+ end
36
+
37
+ Faraday::Response.register_middleware(httpigeon_logger: HTTPigeon::Middleware::HTTPigeonLogger)
@@ -0,0 +1,96 @@
1
+ require "faraday"
2
+ require "active_support/core_ext/hash"
3
+ require "active_support/isolated_execution_state"
4
+ require "active_support/core_ext/time"
5
+ require_relative "middleware/httpigeon_logger"
6
+
7
+ module HTTPigeon
8
+ class Request
9
+ class << self
10
+ def get(endpoint, query = {}, headers = {}, event_type = nil, log_filters = [])
11
+ request = new(base_url: endpoint, headers: headers, event_type: event_type, log_filters: log_filters)
12
+ parsed_response = request.run(method: :get, path: '', payload: query) do |req|
13
+ yield(req) if block_given?
14
+ end
15
+
16
+ HTTPigeon::Response.new(request, parsed_response, request.response)
17
+ end
18
+
19
+ def post(endpoint, payload, headers = {}, event_type = nil, log_filters = [])
20
+ request = new(base_url: endpoint, headers: headers, event_type: event_type, log_filters: log_filters)
21
+ parsed_response = request.run(method: :post, path: '', payload: payload) do |req|
22
+ yield(req) if block_given?
23
+ end
24
+
25
+ HTTPigeon::Response.new(request, parsed_response, request.response)
26
+ end
27
+ end
28
+
29
+ attr_reader :connection, :response, :parsed_response
30
+
31
+ delegate :status, :body, to: :response, prefix: true
32
+
33
+ def initialize(base_url:, options: nil, headers: nil, adapter: nil, logger: nil, event_type: nil, log_filters: nil)
34
+ @base_url = base_url
35
+ @event_type = event_type
36
+ @log_filters = log_filters || []
37
+ @logger = logger || default_logger
38
+
39
+ request_headers = default_headers.merge(headers.to_h)
40
+ base_connection = Faraday.new(url: base_url)
41
+
42
+ @connection = if block_given?
43
+ yield(base_connection) && base_connection
44
+ else
45
+ base_connection.tap do |faraday|
46
+ faraday.headers.deep_merge!(request_headers)
47
+ faraday.options.merge!(options.to_h)
48
+ faraday.request :url_encoded
49
+ faraday.adapter adapter || Faraday.default_adapter
50
+ faraday.response :httpigeon_logger, @logger
51
+ end
52
+ end
53
+ end
54
+
55
+ def run(method: :get, path: '/', payload: {})
56
+ unless method.to_sym == :get
57
+ payload = payload.presence&.to_json
58
+ connection.headers['Content-Type'] = 'application/json'
59
+ end
60
+
61
+ @response = connection.send(method, path, payload) do |request|
62
+ yield(request) if block_given?
63
+ end
64
+
65
+ @parsed_response = parse_response || {}
66
+ end
67
+
68
+ private
69
+
70
+ attr_reader :path, :logger, :event_type, :log_filters
71
+
72
+ def parse_response
73
+ JSON.parse(response_body).with_indifferent_access
74
+ rescue JSON::ParserError
75
+ response_body.presence
76
+ end
77
+
78
+ def default_logger
79
+ HTTPigeon::Logger.new(event_type: event_type, log_filters: log_filters)
80
+ end
81
+
82
+ def default_headers
83
+ HTTPigeon.auto_generate_request_id ? { 'Accept' => 'application/json', 'X-Request-Id' => SecureRandom.uuid } : { 'Accept' => 'application/json' }
84
+ end
85
+ end
86
+
87
+ class Response
88
+ attr_reader :request, :parsed_response, :raw_response
89
+
90
+ def initialize(request, parsed_response, raw_response)
91
+ @request = request
92
+ @parsed_response = parsed_response
93
+ @raw_response = raw_response
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,3 @@
1
+ module HTTPigeon
2
+ VERSION = "2.0.0".freeze
3
+ end
data/lib/httpigeon.rb ADDED
@@ -0,0 +1,43 @@
1
+ require "active_support/core_ext/module/delegation"
2
+
3
+ require "httpigeon/configuration"
4
+ require "httpigeon/version"
5
+ require "httpigeon/log_redactor"
6
+ require "httpigeon/logger"
7
+ require "httpigeon/request"
8
+
9
+ module HTTPigeon
10
+ extend self
11
+
12
+ module FilterPatterns
13
+ EMAIL = "/(?'key'(email_?(address|Address)?=))(?'value'(.*\\.[a-z]+))(&|$)/".freeze
14
+ PASSWORD = "/(?'key'(pass_?(w|W)?ord=))(?'value'([^&$])*)/".freeze
15
+ USERNAME = "/(?'key'(user_?(n|N)?ame=))(?'value'([^&$])*)/".freeze
16
+ CLIENT_ID = "/(?'key'(client_?(id|Id)?=))(?'value'([^&$])*)/".freeze
17
+ CLIENT_SECRET = "/(?'key'(client_?(s|S)?ecret=))(?'value'([^&$])*)/".freeze
18
+ end
19
+
20
+ delegate :default_event_type,
21
+ :default_filter_keys,
22
+ :redactor_string,
23
+ :log_redactor,
24
+ :event_logger,
25
+ :auto_generate_request_id,
26
+ :notify_all_exceptions,
27
+ :exception_notifier,
28
+ to: :configuration
29
+
30
+ def configure
31
+ @config = HTTPigeon::Configuration.new
32
+
33
+ yield(@config)
34
+
35
+ @config.freeze
36
+ end
37
+
38
+ private
39
+
40
+ def configuration
41
+ @configuration ||= @config || HTTPigeon::Configuration.new
42
+ end
43
+ end
metadata ADDED
@@ -0,0 +1,180 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: httpigeon
3
+ version: !ruby/object:Gem::Version
4
+ version: 2.0.0
5
+ platform: ruby
6
+ authors:
7
+ - 2k-joker
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2023-10-25 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: faraday
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 2.7.6
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 2.7.6
27
+ - !ruby/object:Gem::Dependency
28
+ name: activesupport
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 7.0.4
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 7.0.4
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '13.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '13.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.4'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.4'
69
+ - !ruby/object:Gem::Dependency
70
+ name: simplecov
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rubocop
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '1.21'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '1.21'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rubocop-rspec
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '2.24'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '2.24'
111
+ - !ruby/object:Gem::Dependency
112
+ name: pry
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: 0.13.1
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: 0.13.1
125
+ description: Client library that simplifies making and logging HTTP requests and responses.
126
+ This library is built as an abstraction on top of the Faraday ruby client.
127
+ email:
128
+ - opensource@dailypay.com
129
+ executables: []
130
+ extensions: []
131
+ extra_rdoc_files: []
132
+ files:
133
+ - ".github/PULL_REQUEST_TEMPLATE/new_feature_template.md"
134
+ - ".github/workflows/ci.yaml"
135
+ - ".github/workflows/publish.yaml"
136
+ - ".github/workflows/release-please.yaml"
137
+ - ".github/workflows/reviewdog.yaml"
138
+ - ".gitignore"
139
+ - ".rubocop.yml"
140
+ - CHANGELOG.md
141
+ - Gemfile
142
+ - LICENSE
143
+ - README.md
144
+ - Rakefile
145
+ - bin/console
146
+ - bin/setup
147
+ - httpigeon.gemspec
148
+ - lib/httpigeon.rb
149
+ - lib/httpigeon/configuration.rb
150
+ - lib/httpigeon/log_redactor.rb
151
+ - lib/httpigeon/logger.rb
152
+ - lib/httpigeon/middleware/httpigeon_logger.rb
153
+ - lib/httpigeon/request.rb
154
+ - lib/httpigeon/version.rb
155
+ homepage: https://github.com/dailypay/httpigeon
156
+ licenses:
157
+ - MIT
158
+ metadata:
159
+ homepage_uri: https://github.com/dailypay/httpigeon
160
+ source_code_uri: https://github.com/dailypay/httpigeon
161
+ post_install_message:
162
+ rdoc_options: []
163
+ require_paths:
164
+ - lib
165
+ required_ruby_version: !ruby/object:Gem::Requirement
166
+ requirements:
167
+ - - "~>"
168
+ - !ruby/object:Gem::Version
169
+ version: 3.1.0
170
+ required_rubygems_version: !ruby/object:Gem::Requirement
171
+ requirements:
172
+ - - ">="
173
+ - !ruby/object:Gem::Version
174
+ version: '0'
175
+ requirements: []
176
+ rubygems_version: 3.3.26
177
+ signing_key:
178
+ specification_version: 4
179
+ summary: Simple, easy way to make and log HTTP requests and responses
180
+ test_files: []