easyship 0.5.1 → 0.6.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/.rubocop.yml +5 -1
- data/CHANGELOG.md +10 -0
- data/CLAUDE.md +116 -0
- data/README.md +66 -1
- data/lib/easyship/client.rb +33 -15
- data/lib/easyship/configuration.rb +4 -1
- data/lib/easyship/logging/logger.rb +66 -0
- data/lib/easyship/logging/null_logger.rb +37 -0
- data/lib/easyship/version.rb +1 -1
- data/lib/easyship.rb +2 -0
- metadata +5 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 90edef7622104c80daa116acd3a8511e63939faa885ad87913a2749d554f75c6
|
|
4
|
+
data.tar.gz: a771f5e0d156e2aaf7a4dd8603116572d870903b3fb1d6ab5de1e638a55881eb
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: fffafc0e173ec627975fc5acb4ead24044a3633ca66d19255e3fc6f1e1180d4ec733a1464327aa491f5a4710d7d5b802334d225912d203a8ad15c278e22eef4b
|
|
7
|
+
data.tar.gz: 2af5b4ef1969f9dc05595fda224688f0ec1787f2fe3ca516c6119dc4c6f052afc4cbafcf074d3d8a0451e5df16b1a0c957fa385990fe6243237d59f0935a8ad0
|
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [v0.6.0](https://github.com/mmarusyk/easyship/tree/v0.6.0) - 2026-02-28
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- Secure logging module with configurable logger support
|
|
7
|
+
- `NullLogger` as default (quiet by default) to avoid log pollution
|
|
8
|
+
- Abstract logging methods: `log_info`, `log_debug`, `log_warn`, `log_error`, `log_request`, `log_response`
|
|
9
|
+
- Logger configuration option to allow custom loggers (Rails.logger, custom Logger, etc.)
|
|
10
|
+
- Integration tests for logging functionality
|
|
11
|
+
- Security-first logging: no API keys, headers, or request/response bodies logged
|
|
12
|
+
|
|
3
13
|
## [v0.5.1](https://github.com/mmarusyk/easyship/tree/v0.5.1) - 2026-01-24
|
|
4
14
|
|
|
5
15
|
### Fixed
|
data/CLAUDE.md
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# CLAUDE Instructions
|
|
2
|
+
|
|
3
|
+
## Release Instruction
|
|
4
|
+
|
|
5
|
+
### 1. Ensure you are on `main` and it is up-to-date
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
git checkout main
|
|
9
|
+
git pull origin main
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
### 2. Determine the next version
|
|
15
|
+
|
|
16
|
+
Follow [Semantic Versioning](https://semver.org/):
|
|
17
|
+
|
|
18
|
+
| Change type | Example bump |
|
|
19
|
+
|---|---|
|
|
20
|
+
| Bug fixes only | `0.5.1` → `0.5.2` |
|
|
21
|
+
| New backward-compatible features | `0.5.1` → `0.6.0` |
|
|
22
|
+
| Breaking changes | `0.5.1` → `1.0.0` |
|
|
23
|
+
|
|
24
|
+
Current version is in `lib/easyship/version.rb`. Review the `[Unreleased]` section of `CHANGELOG.md` to decide the correct bump type.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
### 3. Update the version constant
|
|
29
|
+
|
|
30
|
+
Edit `lib/easyship/version.rb` and set the new version:
|
|
31
|
+
|
|
32
|
+
```ruby
|
|
33
|
+
module Easyship
|
|
34
|
+
VERSION = 'X.Y.Z' # replace with the new version
|
|
35
|
+
end
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
### 4. Update `CHANGELOG.md`
|
|
41
|
+
|
|
42
|
+
The changelog follows [Keep a Changelog](https://keepachangelog.com/) format.
|
|
43
|
+
|
|
44
|
+
- Rename the `## [Unreleased]` section header to `## [vX.Y.Z](https://github.com/mmarusyk/easyship/tree/vX.Y.Z) - YYYY-MM-DD`
|
|
45
|
+
- Use today's date in `YYYY-MM-DD` format.
|
|
46
|
+
- Add a new empty `## [Unreleased]` section at the top (above the new version section).
|
|
47
|
+
|
|
48
|
+
**Before:**
|
|
49
|
+
```markdown
|
|
50
|
+
## [Unreleased]
|
|
51
|
+
|
|
52
|
+
### Added
|
|
53
|
+
- Some new feature
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
**After:**
|
|
57
|
+
```markdown
|
|
58
|
+
## [Unreleased]
|
|
59
|
+
|
|
60
|
+
## [v0.6.0](https://github.com/mmarusyk/easyship/tree/v0.6.0) - 2026-02-28
|
|
61
|
+
|
|
62
|
+
### Added
|
|
63
|
+
- Some new feature
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
### 5. Run bundle install and tests with linting
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
bundle install
|
|
72
|
+
bundle exec rake
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
This runs both the test suite (`rspec`) and linter (`rubocop`). **Do not proceed if either fails.** Fix all issues before continuing.
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
### 6. Commit the release changes
|
|
80
|
+
|
|
81
|
+
Stage only the version and changelog files:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
git add lib/easyship/version.rb CHANGELOG.md Gemfile.lock
|
|
85
|
+
git commit -m "Release vX.Y.Z"
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
The commit message must follow the pattern `Release vX.Y.Z` exactly (e.g. `Release v0.6.0`).
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
### 7. Push the commit to `main`
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
git push origin main
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
### 8. Create and push the version tag
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
git tag vX.Y.Z
|
|
104
|
+
git push origin vX.Y.Z
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Pushing a tag matching `v*` automatically triggers the `.github/workflows/release.yml` GitHub Actions workflow, which builds and publishes the gem to RubyGems.org using trusted publishing (no manual API key needed).
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
### 9. Verify the release
|
|
112
|
+
|
|
113
|
+
- Check the [Actions tab](https://github.com/mmarusyk/easyship/actions) to confirm the `Release Gem` workflow completed successfully.
|
|
114
|
+
- Confirm the new version appears on [RubyGems.org](https://rubygems.org/gems/easyship).
|
|
115
|
+
|
|
116
|
+
---
|
data/README.md
CHANGED
|
@@ -58,7 +58,72 @@ Easyship.configure do |config|
|
|
|
58
58
|
end
|
|
59
59
|
```
|
|
60
60
|
|
|
61
|
-
Configuration supports the next keys: `url`, `api_key`, `per_page`, `requests_per_second`, `requests_per_minute`, `headers`.
|
|
61
|
+
Configuration supports the next keys: `url`, `api_key`, `per_page`, `requests_per_second`, `requests_per_minute`, `headers`, `logger`.
|
|
62
|
+
|
|
63
|
+
### Logging
|
|
64
|
+
|
|
65
|
+
The gem includes secure, professional logging capabilities that are **quiet by default**. Logging is designed to be abstract and never exposes sensitive data like API keys, headers, or request/response bodies.
|
|
66
|
+
|
|
67
|
+
**Default Behavior (Quiet & Secure):**
|
|
68
|
+
```ruby
|
|
69
|
+
# By default, logging is disabled (NullLogger)
|
|
70
|
+
Easyship.configure do |config|
|
|
71
|
+
config.url = 'api_url'
|
|
72
|
+
config.api_key = 'your_easyship_api_key'
|
|
73
|
+
# No logging output - completely silent
|
|
74
|
+
end
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
**Using Rails Logger:**
|
|
78
|
+
```ruby
|
|
79
|
+
# In config/initializers/easyship.rb
|
|
80
|
+
Easyship.configure do |config|
|
|
81
|
+
config.url = 'api_url'
|
|
82
|
+
config.api_key = 'your_easyship_api_key'
|
|
83
|
+
config.logger = Rails.logger
|
|
84
|
+
end
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
**Using Custom Logger:**
|
|
88
|
+
```ruby
|
|
89
|
+
require 'logger'
|
|
90
|
+
|
|
91
|
+
custom_logger = Logger.new(STDOUT)
|
|
92
|
+
custom_logger.level = Logger::INFO
|
|
93
|
+
|
|
94
|
+
Easyship.configure do |config|
|
|
95
|
+
config.url = 'api_url'
|
|
96
|
+
config.api_key = 'your_easyship_api_key'
|
|
97
|
+
config.logger = custom_logger
|
|
98
|
+
end
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
**Log File Output:**
|
|
102
|
+
```ruby
|
|
103
|
+
Easyship.configure do |config|
|
|
104
|
+
config.url = 'api_url'
|
|
105
|
+
config.api_key = 'your_easyship_api_key'
|
|
106
|
+
config.logger = Logger.new('log/easyship.log')
|
|
107
|
+
end
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
**Example Log Output:**
|
|
111
|
+
```
|
|
112
|
+
[Easyship] GET /2023-01/account
|
|
113
|
+
[Easyship] Response 200 (0.245s)
|
|
114
|
+
[Easyship] POST /2023-01/shipments
|
|
115
|
+
[Easyship] Response 201 (0.432s)
|
|
116
|
+
[Easyship] Error: InvalidTokenError - Invalid API token | Context: {:method=>:get, :path=>"/2023-01/account"}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
**Extensibility:**
|
|
120
|
+
You can extend logger module for custom logging needs:
|
|
121
|
+
- `log_info(logger, message)` - General info logging
|
|
122
|
+
- `log_debug(logger, message)` - Debug logging
|
|
123
|
+
- `log_warn(logger, message)` - Warning logging
|
|
124
|
+
- `log_error(logger, error, context)` - Error with context
|
|
125
|
+
- `log_request(logger, method, path)` - Request logging
|
|
126
|
+
- `log_response(logger, status, duration)` - Response logging
|
|
62
127
|
|
|
63
128
|
### Making Requests
|
|
64
129
|
`Easyship::Client` supports the next methods: `get`, `post`, `put`, `patch`, `delete`.
|
data/lib/easyship/client.rb
CHANGED
|
@@ -5,53 +5,71 @@ require 'faraday'
|
|
|
5
5
|
require_relative 'middleware/error_handler_middleware'
|
|
6
6
|
require_relative 'handlers/response_body_handler'
|
|
7
7
|
require_relative 'pagination/cursor'
|
|
8
|
+
require_relative 'logging/logger'
|
|
8
9
|
|
|
9
10
|
module Easyship
|
|
10
11
|
# Represents a client to interact with the Easyship API
|
|
11
12
|
class Client
|
|
12
13
|
include Singleton
|
|
14
|
+
include Easyship::Logging::Logger
|
|
13
15
|
|
|
14
16
|
def initialize
|
|
15
17
|
@url = Easyship.configuration.url
|
|
16
18
|
@api_key = Easyship.configuration.api_key
|
|
19
|
+
@logger = Easyship.configuration.logger
|
|
17
20
|
end
|
|
18
21
|
|
|
19
22
|
def get(path, params = {}, headers: {}, &block)
|
|
20
23
|
if block
|
|
21
24
|
Easyship::Pagination::Cursor.new(self, path, params).all(&block)
|
|
22
25
|
else
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
+
execute_request(:get, path, params, headers) do |conn|
|
|
27
|
+
conn.get(path, params)
|
|
28
|
+
end
|
|
26
29
|
end
|
|
27
30
|
end
|
|
28
31
|
|
|
29
32
|
def post(path, params = {}, headers: {})
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
+
execute_request(:post, path, params, headers) do |conn|
|
|
34
|
+
conn.post(path, params.to_json)
|
|
35
|
+
end
|
|
33
36
|
end
|
|
34
37
|
|
|
35
38
|
def put(path, params = {}, headers: {})
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
+
execute_request(:put, path, params, headers) do |conn|
|
|
40
|
+
conn.put(path, params.to_json)
|
|
41
|
+
end
|
|
39
42
|
end
|
|
40
43
|
|
|
41
44
|
def delete(path, params = {}, headers: {})
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
+
execute_request(:delete, path, params, headers) do |conn|
|
|
46
|
+
conn.delete(path, params)
|
|
47
|
+
end
|
|
45
48
|
end
|
|
46
49
|
|
|
47
50
|
def patch(path, params = {}, headers: {})
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
+
execute_request(:patch, path, params, headers) do |conn|
|
|
52
|
+
conn.patch(path, params.to_json)
|
|
53
|
+
end
|
|
51
54
|
end
|
|
52
55
|
|
|
53
56
|
private
|
|
54
57
|
|
|
58
|
+
def execute_request(method, path, _params, headers)
|
|
59
|
+
log_request(@logger, method, path)
|
|
60
|
+
|
|
61
|
+
start_time = Time.now
|
|
62
|
+
response = yield(connection(headers))
|
|
63
|
+
duration = Time.now - start_time
|
|
64
|
+
|
|
65
|
+
log_response(@logger, response.status, duration)
|
|
66
|
+
|
|
67
|
+
handle_response(response)
|
|
68
|
+
rescue StandardError => e
|
|
69
|
+
log_error(@logger, e, { method: method, path: path })
|
|
70
|
+
raise
|
|
71
|
+
end
|
|
72
|
+
|
|
55
73
|
def connection(custom_headers = {})
|
|
56
74
|
Faraday.new(url: @url) do |faraday|
|
|
57
75
|
faraday.request :url_encoded
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative 'logging/null_logger'
|
|
4
|
+
|
|
3
5
|
module Easyship
|
|
4
6
|
# Represents the configuration settings for the Easyship client.
|
|
5
7
|
class Configuration
|
|
6
|
-
attr_accessor :url, :api_key, :per_page, :requests_per_second, :requests_per_minute, :headers
|
|
8
|
+
attr_accessor :url, :api_key, :per_page, :requests_per_second, :requests_per_minute, :headers, :logger
|
|
7
9
|
|
|
8
10
|
def initialize
|
|
9
11
|
@url = nil
|
|
@@ -12,6 +14,7 @@ module Easyship
|
|
|
12
14
|
@requests_per_second = nil
|
|
13
15
|
@requests_per_minute = nil
|
|
14
16
|
@headers = {}
|
|
17
|
+
@logger = Easyship::Logging::NullLogger.new
|
|
15
18
|
end
|
|
16
19
|
end
|
|
17
20
|
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'logger'
|
|
4
|
+
|
|
5
|
+
module Easyship
|
|
6
|
+
module Logging
|
|
7
|
+
# This module provides logging methods that can be included in any class
|
|
8
|
+
# to add secure, standardized logging capabilities.
|
|
9
|
+
module Logger
|
|
10
|
+
def log_info(logger, message)
|
|
11
|
+
return unless logger
|
|
12
|
+
|
|
13
|
+
logger.info("[Easyship] #{message}")
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def log_debug(logger, message)
|
|
17
|
+
return unless logger
|
|
18
|
+
|
|
19
|
+
logger.debug("[Easyship] #{message}")
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def log_warn(logger, message)
|
|
23
|
+
return unless logger
|
|
24
|
+
|
|
25
|
+
logger.warn("[Easyship] #{message}")
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def log_error(logger, error, context = {})
|
|
29
|
+
return unless logger
|
|
30
|
+
|
|
31
|
+
logger.error do
|
|
32
|
+
message = "[Easyship] Error: #{error.class} - #{error.message}"
|
|
33
|
+
message += " | Context: #{context}" if context.any?
|
|
34
|
+
message += "\n#{error.backtrace.join("\n")}" if error.backtrace
|
|
35
|
+
message
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def log_request(logger, method, path)
|
|
40
|
+
return unless logger
|
|
41
|
+
|
|
42
|
+
logger.info("[Easyship] #{method.to_s.upcase} #{path}")
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def log_response(logger, status, duration = nil)
|
|
46
|
+
return unless logger
|
|
47
|
+
|
|
48
|
+
level = response_log_level(status)
|
|
49
|
+
message = "[Easyship] Response #{status}"
|
|
50
|
+
message += " (#{format('%.3f', duration)}s)" if duration
|
|
51
|
+
|
|
52
|
+
logger.public_send(level, message)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
private
|
|
56
|
+
|
|
57
|
+
def response_log_level(status)
|
|
58
|
+
return :warn if status.between?(400, 499)
|
|
59
|
+
return :error if status.between?(500, 599)
|
|
60
|
+
return :debug if status.between?(200, 299)
|
|
61
|
+
|
|
62
|
+
:info
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Easyship
|
|
4
|
+
module Logging
|
|
5
|
+
# Implements the standard Logger interface but performs no actual logging,
|
|
6
|
+
# making it safe to use as a default without any output or performance impact.
|
|
7
|
+
class NullLogger
|
|
8
|
+
def debug(_message = nil, &); end
|
|
9
|
+
|
|
10
|
+
def info(_message = nil, &); end
|
|
11
|
+
|
|
12
|
+
def warn(_message = nil, &); end
|
|
13
|
+
|
|
14
|
+
def error(_message = nil, &); end
|
|
15
|
+
|
|
16
|
+
def fatal(_message = nil, &); end
|
|
17
|
+
|
|
18
|
+
def unknown(_message = nil, &); end
|
|
19
|
+
|
|
20
|
+
def level=(_level); end
|
|
21
|
+
|
|
22
|
+
def level
|
|
23
|
+
::Logger::UNKNOWN
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def tagged(*_tags)
|
|
27
|
+
yield self if block_given?
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def formatter=(_formatter); end
|
|
31
|
+
|
|
32
|
+
def formatter
|
|
33
|
+
nil
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
data/lib/easyship/version.rb
CHANGED
data/lib/easyship.rb
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
require_relative 'easyship/version'
|
|
4
4
|
require_relative 'easyship/configuration'
|
|
5
|
+
require_relative 'easyship/logging/null_logger'
|
|
6
|
+
require_relative 'easyship/logging/logger'
|
|
5
7
|
require_relative 'easyship/client'
|
|
6
8
|
require_relative 'easyship/rate_limiting/rate_limiter'
|
|
7
9
|
require_relative 'easyship/rate_limiting/window_rate_limiter'
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: easyship
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.6.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Michael Marusyk
|
|
8
8
|
bindir: exe
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-
|
|
10
|
+
date: 2026-02-28 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: faraday
|
|
@@ -35,6 +35,7 @@ files:
|
|
|
35
35
|
- ".rspec"
|
|
36
36
|
- ".rubocop.yml"
|
|
37
37
|
- CHANGELOG.md
|
|
38
|
+
- CLAUDE.md
|
|
38
39
|
- CODE_OF_CONDUCT.md
|
|
39
40
|
- CONTRIBUTORS.md
|
|
40
41
|
- LICENSE.txt
|
|
@@ -54,6 +55,8 @@ files:
|
|
|
54
55
|
- lib/easyship/errors/server_error.rb
|
|
55
56
|
- lib/easyship/errors/unprocessable_content_error.rb
|
|
56
57
|
- lib/easyship/handlers/response_body_handler.rb
|
|
58
|
+
- lib/easyship/logging/logger.rb
|
|
59
|
+
- lib/easyship/logging/null_logger.rb
|
|
57
60
|
- lib/easyship/middleware/error_handler_middleware.rb
|
|
58
61
|
- lib/easyship/pagination/cursor.rb
|
|
59
62
|
- lib/easyship/rate_limiting/rate_limiter.rb
|