Skip to content

Commit 611217d

Browse files
committed
[NEP-20726] Dependency Pruning, Faraday 2.x and ruby compatibility
1 parent 7bb2390 commit 611217d

File tree

15 files changed

+2592
-26
lines changed

15 files changed

+2592
-26
lines changed

.devcontainer/devcontainer.json

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
2+
// README at: https://github.com/devcontainers/templates/tree/main/src/ruby
3+
{
4+
"name": "happi_dev",
5+
"image": "mcr.microsoft.com/devcontainers/ruby:1-3.4-bullseye",
6+
// Features to add to the dev container. More info: https://containers.dev/features.
7+
// "features": {},
8+
9+
// Use 'forwardPorts' to make a list of ports inside the container available locally.
10+
// "forwardPorts": [],
11+
12+
// Use 'postCreateCommand' to run commands after the container is created.
13+
"postCreateCommand": "bundle install",
14+
15+
"mounts": [
16+
"source=happi-bundle,target=/usr/local/bundle,type=volume"
17+
]
18+
19+
// Configure tool-specific properties.
20+
// "customizations": {},
21+
22+
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
23+
//"remoteUser": "root"
24+
}

.github/copilot-instructions.md

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
# Happi - AI Coding Instructions
2+
3+
## Project Overview
4+
Happi is a lightweight Ruby gem that provides a pre-configured Faraday HTTP client wrapper for RESTful APIs. It assumes URL patterns like `https://hostname.com/api/v1/something` and handles OAuth2 authentication, multipart file uploads, and JSON/form-encoded requests.
5+
6+
## Architecture & Design Patterns
7+
8+
### Class-Based Configuration (Critical)
9+
**Never use `Happi::Client` directly** - always create a derived class. Configuration is stored as class-level state, so using the base class directly causes cross-contamination between different API endpoints.
10+
11+
```ruby
12+
# CORRECT: Derive your own client
13+
class MyClient < Happi::Client; end
14+
MyClient.configure { |config| config.host = 'http://api.example.com' }
15+
16+
# WRONG: Never use base class directly
17+
Happi::Client.configure { ... } # Will pollute all clients
18+
```
19+
20+
This pattern is tested extensively in [spec/client_spec.rb](spec/client_spec.rb#L5-L85) which validates config isolation between base/derived classes at both class and instance levels.
21+
22+
### Configuration Hierarchy
23+
Config precedence: instance-level > class-level > defaults (from `Happi::Configuration.defaults`).
24+
25+
Instance config is set via initializer: `MyClient.new(oauth_token: 'abc123', timeout: 30)`
26+
27+
## Key Components
28+
29+
- **[lib/happi/client.rb](lib/happi/client.rb)**: Main HTTP client with REST verbs (get/post/patch/delete), error handling, OAuth2 middleware integration
30+
- **[lib/happi/configuration.rb](lib/happi/configuration.rb)**: Config object with defaults for host, port, timeout, version, use_json, log_level, token_type
31+
- **[lib/happi/file.rb](lib/happi/file.rb)**: Handles file uploads with MIME type detection, converts to Faraday::UploadIO for multipart requests
32+
- **[lib/happi/error.rb](lib/happi/error.rb)**: HTTP error hierarchy mapping status codes (400-504) to specific exception classes
33+
34+
## Developer Workflows
35+
36+
### Testing
37+
```bash
38+
# Run all specs
39+
bundle exec rspec
40+
41+
# Run specific spec file
42+
bundle exec rspec spec/client_spec.rb
43+
```
44+
45+
RSpec is configured with SimpleCov for coverage reports (see [coverage/index.html](coverage/index.html)).
46+
47+
### Gemfile Variations
48+
- `Gemfile`: Standard dependencies
49+
- `Gemfile.rails32`, `Gemfile.rails41`: Legacy Rails compatibility testing (historical, not actively maintained)
50+
51+
## Critical Conventions
52+
53+
### Multipart File Handling
54+
When a hash value responds to `#multipart`, it's automatically converted by `param_check` method. This enables seamless file uploads:
55+
56+
```ruby
57+
client.post('templates', template: {
58+
name: 'test',
59+
file: Happi::File.new('/path/to/file.docx') # Automatically becomes multipart
60+
})
61+
```
62+
63+
### JSON vs Form Encoding
64+
Controlled by `config.use_json` flag:
65+
- `false` (default): Uses multipart/form-encoded requests
66+
- `true`: Encodes requests as JSON and parses JSON responses
67+
68+
### OAuth2 Token Types
69+
Set `token_type: 'bearer'` to pass OAuth tokens only as Authorization header (not as query param). This avoids faraday_middleware warnings. See [CHANGELOG.md](CHANGELOG.md#L30) for context.
70+
71+
### Logging Behavior
72+
- `log_level: :debug` logs full request bodies/params (can generate large logs)
73+
- `log_level: :info` (default) logs only HTTP method and URL
74+
- Rails logger auto-detected via `Rails.try(:logger)`, falls back to STDOUT
75+
76+
### URL Construction
77+
URLs automatically prefixed with `/api/#{version}/`. The `version` config defaults to `'v1'`. Example:
78+
```ruby
79+
client.get('templates') # Requests /api/v1/templates
80+
```
81+
82+
## Dependencies
83+
- **faraday ~> 2.13**: Core HTTP client
84+
- **oauth2 ~> 2.0**: Authentication (via FaradayMiddleware::OAuth2)
85+
- **activemodel >= 6.0**: Provides `with_indifferent_access` for response hashes
86+
- **mime-types**: File MIME type detection
87+
- Ruby >= 3.2.0
88+
89+
## Testing Patterns
90+
Specs extensively test configuration isolation, multipart param checking, and connection options. When adding features:
91+
1. Test class-level vs instance-level config behavior
92+
2. Verify derived classes don't pollute base class state
93+
3. Use fixtures from [spec/fixtures/](spec/fixtures/) for file upload tests

.github/workflows/ruby.yml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
name: Ruby
2+
3+
on:
4+
pull_request:
5+
push:
6+
branches: [ "develop" ]
7+
8+
permissions:
9+
contents: read
10+
11+
jobs:
12+
spec:
13+
runs-on: ubuntu-latest
14+
strategy:
15+
fail-fast: false
16+
matrix:
17+
ruby-version: ["3.2", "3.3", "3.4", "4.0"]
18+
19+
steps:
20+
- uses: actions/checkout@v4
21+
- uses: ruby/setup-ruby@v1
22+
with:
23+
ruby-version: ${{ matrix.ruby-version }}
24+
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
25+
- name: RSpec
26+
run: bundle exec rake spec

.ruby-version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
3.2.0
1+
3.4.8

.travis.yml

Lines changed: 0 additions & 12 deletions
This file was deleted.

CHANGELOG.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,20 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
66

77
## [Unreleased]
88

9+
## [1.0.0-rc2] - 2026-01-09
10+
### Changed
11+
- **BREAKING**: Removed unused dependencies: `oauth2`, `multi_json`, `faraday-follow_redirects`, and `faraday-http`
12+
- Changed dependency from `activemodel` to `activesupport` (only ActiveSupport features were used)
13+
- Applications using these gems directly must add them to their own Gemfile
14+
15+
### Fixed
16+
- Fixed compatibility with faraday 2.x by removing FaradayMiddleware
17+
918
## [1.0.0-rc] - 2025-07-07
1019
### Updated
1120
- Updated all gem dependencies
1221
- Set ruby version min to 3.2.0
1322

14-
1523
## [0.6.0] - 2024-11-13
1624
### Added
1725
- Adds support for Ruby 3.2
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
# Faraday 2.x Compatibility Report for Happi Gem
2+
3+
## Issue Validation ✅ RESOLVED
4+
5+
### Test Results
6+
Added compatibility tests to [spec/client_spec.rb](spec/client_spec.rb#L105-L130) that now pass:
7+
8+
```
9+
4 examples, 0 failures
10+
11+
Happi::Client
12+
#connection
13+
Faraday 2.x compatibility
14+
creates a Faraday connection without FaradayMiddleware errors
15+
returns a Faraday::Connection instance
16+
properly sets OAuth2 Authorization header
17+
with custom token_type
18+
uses the specified token_type
19+
```
20+
21+
All 41 tests in the full suite pass.
22+
23+
### Root Cause
24+
The gem currently uses `FaradayMiddleware::OAuth2` and `FaradayMiddleware::EncodeJson` on [lib/happi/client.rb](lib/happi/client.rb#L99-L103), but:
25+
26+
1. **`faraday_middleware` gem is not in dependencies** - [happi.gemspec](happi.gemspec) only includes `oauth2 ~> 2.0` but not `faraday_middleware`
27+
2. **`faraday_middleware` is incompatible with Faraday 2.x** - As documented in [context/faraday_2.x.md](context/faraday_2.x.md), the `faraday_middleware` gem was NOT updated to support Faraday 2.0+
28+
29+
## Recommended Compatibility Adjustments
30+
31+
### ✅ IMPLEMENTED: Replaced OAuth2 Middleware
32+
33+
Replaced `FaradayMiddleware::OAuth2` with manual Authorization header setting in [lib/happi/client.rb](lib/happi/client.rb#L98-L115):
34+
35+
```ruby
36+
def connection
37+
@connection ||= Faraday.new(config.host) do |f|
38+
# Set OAuth2 Authorization header
39+
if config.oauth_token.present?
40+
token_type = config.token_type.presence || 'Bearer'
41+
f.headers['Authorization'] = "#{token_type} #{config.oauth_token}"
42+
end
43+
44+
if self.config.use_json
45+
f.request :json # Encodes request body as JSON
46+
f.response :json # Parses JSON responses
47+
else
48+
f.request :multipart
49+
f.request :url_encoded
50+
end
51+
52+
f.adapter :net_http
53+
end
54+
end
55+
```
56+
57+
### ✅ IMPLEMENTED: Removed EncodeJson Middleware
58+
59+
Removed `FaradayMiddleware::EncodeJson` - Faraday 2.x has native JSON request/response handling via `f.request :json` and `f.response :json`.
60+
61+
### ✅ IMPLEMENTED: Removed Custom JSON Middleware
62+
63+
Removed the undefined `JSON` constant middleware - Faraday's native JSON handling provides the same functionality.
64+
65+
## Additional Recommendations
66+
67+
### 5. Update Dependencies (Optional but Recommended)
68+
69+
Consider adding these modern Faraday middleware gems if you need their features:
70+
```ruby
71+
# In happi.gemspec
72+
spec.add_dependency 'faraday-retry', '~> 2.0' # For automatic retries
73+
```
74+
75+
### 6. Update Documentation
76+
77+
Update the following files to remove `FaradayMiddleware` references:
78+
- [CHANGELOG.md](CHANGELOG.md#L30) - Update notes about `FaradayMiddleware::OAuth2`
79+
- [.github/copilot-instructions.md](.github/copilot-instructions.md#L84) - Update dependency description
80+
81+
### 7. Test Coverage
82+
83+
After implementing changes, ensure tests pass:
84+
```bash
85+
bundle exec rspec spec/client_spec.rb:105
86+
bundle exec rspec # Run all tests
87+
```
88+
89+
## Migration Priority
90+
91+
1. **HIGH**: Remove `FaradayMiddleware::OAuth2` usage (breaks all OAuth requests)
92+
2. **HIGH**: Remove `FaradayMiddleware::EncodeJson` usage (breaks JSON requests)
93+
3. **MEDIUM**: Remove/define custom `JSON` middleware
94+
4. **LOW**: Update documentation
95+
96+
## Compatibility Matrix
97+
98+
| Component | Current | Status | Recommendation |
99+
|-----------|---------|--------|----------------|
100+
| Faraday | 2.13 | ✅ OK | Keep current |
101+
| FaradayMiddleware | Not included | ❌ BROKEN | Remove usage |
102+
| oauth2 gem | 2.0 | ✅ OK | Use directly |
103+
| JSON encoding | Mixed | ⚠️ PARTIAL | Use Faraday native |
104+
105+
## Next Steps
106+
107+
1. ✅ Validation complete - tests added and failing as expected
108+
2. ✅ Implement OAuth2 header solution - Manual Authorization header implemented
109+
3. ✅ Remove `FaradayMiddleware::EncodeJson` line - Removed
110+
4. ✅ Address custom `JSON` middleware - Removed undefined constant
111+
5. ✅ Run tests to verify fixes - All 41 tests pass
112+
6. ⏳ Update documentation (optional)

0 commit comments

Comments
 (0)