petstore_api_client 0.1.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 +7 -0
- data/.editorconfig +33 -0
- data/.env.example +50 -0
- data/.github/CODEOWNERS +36 -0
- data/.github/workflows/ci.yml +157 -0
- data/.ruby-version +1 -0
- data/CONTRIBUTORS.md +39 -0
- data/LICENSE +21 -0
- data/README.md +684 -0
- data/Rakefile +12 -0
- data/lib/petstore_api_client/api_client.rb +60 -0
- data/lib/petstore_api_client/authentication/api_key.rb +107 -0
- data/lib/petstore_api_client/authentication/base.rb +113 -0
- data/lib/petstore_api_client/authentication/composite.rb +178 -0
- data/lib/petstore_api_client/authentication/none.rb +42 -0
- data/lib/petstore_api_client/authentication/oauth2.rb +305 -0
- data/lib/petstore_api_client/client.rb +87 -0
- data/lib/petstore_api_client/clients/concerns/pagination.rb +124 -0
- data/lib/petstore_api_client/clients/concerns/resource_operations.rb +121 -0
- data/lib/petstore_api_client/clients/pet_client.rb +119 -0
- data/lib/petstore_api_client/clients/store_client.rb +37 -0
- data/lib/petstore_api_client/configuration.rb +318 -0
- data/lib/petstore_api_client/connection.rb +55 -0
- data/lib/petstore_api_client/errors.rb +70 -0
- data/lib/petstore_api_client/middleware/authentication.rb +44 -0
- data/lib/petstore_api_client/models/api_response.rb +31 -0
- data/lib/petstore_api_client/models/base.rb +60 -0
- data/lib/petstore_api_client/models/category.rb +17 -0
- data/lib/petstore_api_client/models/named_entity.rb +36 -0
- data/lib/petstore_api_client/models/order.rb +55 -0
- data/lib/petstore_api_client/models/pet.rb +225 -0
- data/lib/petstore_api_client/models/tag.rb +20 -0
- data/lib/petstore_api_client/paginated_collection.rb +133 -0
- data/lib/petstore_api_client/request.rb +225 -0
- data/lib/petstore_api_client/response.rb +193 -0
- data/lib/petstore_api_client/validators/array_presence_validator.rb +15 -0
- data/lib/petstore_api_client/validators/enum_validator.rb +17 -0
- data/lib/petstore_api_client/version.rb +5 -0
- data/lib/petstore_api_client.rb +55 -0
- metadata +252 -0
data/README.md
ADDED
|
@@ -0,0 +1,684 @@
|
|
|
1
|
+
# ๐พ Petstore API Client
|
|
2
|
+
|
|
3
|
+
[](https://www.ruby-lang.org/)
|
|
4
|
+
[](https://rspec.info/)
|
|
5
|
+
[](https://github.com/hammadxcm/petstore-api-client)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
[](https://github.com/hammadxcm)
|
|
8
|
+
|
|
9
|
+
> Production-ready Ruby client for the Swagger Petstore API with OAuth2 support, automatic retries, and comprehensive validation.
|
|
10
|
+
>
|
|
11
|
+
> **Author:** Hammad Khan ([@hammadxcm](https://github.com/hammadxcm))
|
|
12
|
+
>
|
|
13
|
+
> **Note:** All architecture, business logic, implementation, and test coverage were developed by me from scratch. AI tools were used solely to enhance documentation and code comments.
|
|
14
|
+
|
|
15
|
+
## ๐ Quick Start
|
|
16
|
+
|
|
17
|
+
```ruby
|
|
18
|
+
gem install petstore_api_client
|
|
19
|
+
|
|
20
|
+
require 'petstore_api_client'
|
|
21
|
+
|
|
22
|
+
# Create client
|
|
23
|
+
client = PetstoreApiClient::ApiClient.new
|
|
24
|
+
|
|
25
|
+
# Create a pet
|
|
26
|
+
pet = client.create_pet(
|
|
27
|
+
name: "Fluffy",
|
|
28
|
+
photo_urls: ["https://example.com/fluffy.jpg"],
|
|
29
|
+
status: "available"
|
|
30
|
+
)
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## โจ Features
|
|
34
|
+
|
|
35
|
+
| Feature | Description |
|
|
36
|
+
|----------------------------|--------------------------------------------|
|
|
37
|
+
| ๐ **Dual Authentication** | API Key & OAuth2 (Client Credentials) |
|
|
38
|
+
| ๐ **Auto Retry** | Exponential backoff for transient failures |
|
|
39
|
+
| โก **Rate Limiting** | Smart handling with retry-after support |
|
|
40
|
+
| ๐ **Pagination** | Flexible page/offset navigation |
|
|
41
|
+
| โ
**Validation** | Pre-request data validation |
|
|
42
|
+
| ๐ก๏ธ **Error Handling** | 7 custom exception types |
|
|
43
|
+
| ๐ **Test Coverage** | 96.91% coverage, 454 passing tests |
|
|
44
|
+
| ๐ฏ **SOLID Design** | Production-ready architecture |
|
|
45
|
+
|
|
46
|
+
## ๐ฆ Installation
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
# Gemfile
|
|
50
|
+
gem 'petstore_api_client'
|
|
51
|
+
|
|
52
|
+
# Install
|
|
53
|
+
bundle install
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## ๐ Authentication
|
|
57
|
+
|
|
58
|
+
The client supports **multiple authentication strategies** with feature flags:
|
|
59
|
+
|
|
60
|
+
```mermaid
|
|
61
|
+
graph TD
|
|
62
|
+
A[Configure Auth Strategy] --> B{Which Strategy?}
|
|
63
|
+
B -->|:none| C[No Authentication]
|
|
64
|
+
B -->|:api_key| D[API Key Only]
|
|
65
|
+
B -->|:oauth2| E[OAuth2 Only]
|
|
66
|
+
B -->|:both| F[API Key + OAuth2]
|
|
67
|
+
|
|
68
|
+
D --> G[Add api_key Header]
|
|
69
|
+
E --> H[Fetch OAuth2 Token]
|
|
70
|
+
H --> I[Add Authorization: Bearer]
|
|
71
|
+
F --> J[Add Both Headers]
|
|
72
|
+
|
|
73
|
+
style B fill:#e3f2fd
|
|
74
|
+
style D fill:#fff3e0
|
|
75
|
+
style E fill:#c8e6c9
|
|
76
|
+
style F fill:#f3e5f5
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### ๐ API Key Authentication
|
|
80
|
+
|
|
81
|
+
```ruby
|
|
82
|
+
client = PetstoreApiClient::ApiClient.new
|
|
83
|
+
client.configure do |config|
|
|
84
|
+
config.auth_strategy = :api_key # Default
|
|
85
|
+
config.api_key = "special-key"
|
|
86
|
+
end
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
**From Environment:**
|
|
90
|
+
```bash
|
|
91
|
+
export PETSTORE_API_KEY="your-key"
|
|
92
|
+
```
|
|
93
|
+
```ruby
|
|
94
|
+
config.api_key = :from_env # Loads from PETSTORE_API_KEY
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### ๐ซ OAuth2 Authentication
|
|
98
|
+
|
|
99
|
+
```ruby
|
|
100
|
+
client.configure do |config|
|
|
101
|
+
config.auth_strategy = :oauth2
|
|
102
|
+
config.oauth2_client_id = "my-client-id"
|
|
103
|
+
config.oauth2_client_secret = "my-secret"
|
|
104
|
+
config.oauth2_scope = "read:pets write:pets" # Optional
|
|
105
|
+
end
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
**OAuth2 Flow:**
|
|
109
|
+
```mermaid
|
|
110
|
+
sequenceDiagram
|
|
111
|
+
participant App as Your App
|
|
112
|
+
participant Client as API Client
|
|
113
|
+
participant Auth as OAuth2 Strategy
|
|
114
|
+
participant Token as Token Server
|
|
115
|
+
participant API as Petstore API
|
|
116
|
+
|
|
117
|
+
App->>Client: create_pet(data)
|
|
118
|
+
Client->>Auth: apply(request)
|
|
119
|
+
|
|
120
|
+
alt Token Missing/Expired
|
|
121
|
+
Auth->>Token: POST /oauth/token
|
|
122
|
+
Token-->>Auth: access_token + expires_in
|
|
123
|
+
Auth->>Auth: Cache token
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
Auth->>Auth: Add Authorization: Bearer {token}
|
|
127
|
+
Client->>API: POST /pet (with Bearer token)
|
|
128
|
+
API-->>Client: 200 OK + Pet data
|
|
129
|
+
Client-->>App: Pet object
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
**Environment Variables:**
|
|
133
|
+
```bash
|
|
134
|
+
export PETSTORE_OAUTH2_CLIENT_ID="my-client-id"
|
|
135
|
+
export PETSTORE_OAUTH2_CLIENT_SECRET="my-secret"
|
|
136
|
+
export PETSTORE_OAUTH2_TOKEN_URL="https://custom.com/token" # Optional
|
|
137
|
+
export PETSTORE_OAUTH2_SCOPE="read:pets write:pets" # Optional
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### ๐ Dual Authentication (Both)
|
|
141
|
+
|
|
142
|
+
Send **both** API Key and OAuth2 headers simultaneously:
|
|
143
|
+
|
|
144
|
+
```ruby
|
|
145
|
+
client.configure do |config|
|
|
146
|
+
config.auth_strategy = :both
|
|
147
|
+
config.api_key = "special-key"
|
|
148
|
+
config.oauth2_client_id = "client-id"
|
|
149
|
+
config.oauth2_client_secret = "secret"
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# Requests will include:
|
|
153
|
+
# - api_key: special-key
|
|
154
|
+
# - Authorization: Bearer {access_token}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### ๐ซ No Authentication
|
|
158
|
+
|
|
159
|
+
```ruby
|
|
160
|
+
config.auth_strategy = :none # No auth headers
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### ๐ Security Features
|
|
164
|
+
|
|
165
|
+
| Feature | Description |
|
|
166
|
+
|-----------------------------|----------------------------------------------|
|
|
167
|
+
| โ
**Credential Validation** | Format & length checks |
|
|
168
|
+
| โ
**HTTPS Warnings** | Alerts for insecure connections |
|
|
169
|
+
| โ
**Secure Logging** | API keys masked in output (e.g., `spec****`) |
|
|
170
|
+
| โ
**Token Auto-Refresh** | OAuth2 tokens refreshed 60s before expiry |
|
|
171
|
+
| โ
**Thread-Safe** | Mutex-protected token management |
|
|
172
|
+
|
|
173
|
+
## โ๏ธ Configuration
|
|
174
|
+
|
|
175
|
+
<details>
|
|
176
|
+
<summary><b>๐ All Configuration Options</b></summary>
|
|
177
|
+
|
|
178
|
+
| Option | Type | Default | Description |
|
|
179
|
+
|------------------------|---------|-------------------------------------------|-----------------------------------------|
|
|
180
|
+
| `base_url` | String | `https://petstore.swagger.io/v2` | API endpoint |
|
|
181
|
+
| `auth_strategy` | Symbol | `:api_key` | `:none`, `:api_key`, `:oauth2`, `:both` |
|
|
182
|
+
| `api_key` | String | `nil` | API key for authentication |
|
|
183
|
+
| `oauth2_client_id` | String | `nil` | OAuth2 client ID |
|
|
184
|
+
| `oauth2_client_secret` | String | `nil` | OAuth2 client secret |
|
|
185
|
+
| `oauth2_token_url` | String | `https://petstore.swagger.io/oauth/token` | OAuth2 token endpoint |
|
|
186
|
+
| `oauth2_scope` | String | `nil` | OAuth2 scope |
|
|
187
|
+
| `timeout` | Integer | `30` | Request timeout (seconds) |
|
|
188
|
+
| `open_timeout` | Integer | `10` | Connection timeout (seconds) |
|
|
189
|
+
| `retry_enabled` | Boolean | `true` | Enable auto-retry |
|
|
190
|
+
| `max_retries` | Integer | `2` | Retry attempts |
|
|
191
|
+
| `default_page_size` | Integer | `25` | Pagination page size |
|
|
192
|
+
| `max_page_size` | Integer | `100` | Max pagination size |
|
|
193
|
+
|
|
194
|
+
</details>
|
|
195
|
+
|
|
196
|
+
```ruby
|
|
197
|
+
client = PetstoreApiClient::ApiClient.new
|
|
198
|
+
client.configure do |config|
|
|
199
|
+
config.timeout = 60
|
|
200
|
+
config.retry_enabled = true
|
|
201
|
+
config.max_retries = 3
|
|
202
|
+
end
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
## ๐ Auto-Retry & Rate Limiting
|
|
206
|
+
|
|
207
|
+
```mermaid
|
|
208
|
+
flowchart LR
|
|
209
|
+
A[Request] --> B{Success?}
|
|
210
|
+
B -->|2xx| C[โ
Return]
|
|
211
|
+
B -->|429/5xx| D{Retries<br/>Left?}
|
|
212
|
+
B -->|4xx| E[โ Error]
|
|
213
|
+
|
|
214
|
+
D -->|Yes| F[Wait +<br/>Backoff]
|
|
215
|
+
D -->|No| G[โ Raise<br/>Error]
|
|
216
|
+
|
|
217
|
+
F --> A
|
|
218
|
+
|
|
219
|
+
style C fill:#c8e6c9
|
|
220
|
+
style E fill:#ffcdd2
|
|
221
|
+
style G fill:#ffcdd2
|
|
222
|
+
style F fill:#fff9c4
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
**Handles:**
|
|
226
|
+
- ๐ Network failures
|
|
227
|
+
- โฑ๏ธ Timeouts
|
|
228
|
+
- ๐ฆ Rate limits (429)
|
|
229
|
+
- ๐ง Server errors (500, 502, 503, 504)
|
|
230
|
+
|
|
231
|
+
```ruby
|
|
232
|
+
begin
|
|
233
|
+
pet = client.get_pet(123)
|
|
234
|
+
rescue PetstoreApiClient::RateLimitError => e
|
|
235
|
+
puts "Retry after: #{e.retry_after}s"
|
|
236
|
+
end
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
## ๐ Usage Examples
|
|
240
|
+
|
|
241
|
+
### ๐ Pet Management
|
|
242
|
+
|
|
243
|
+
```ruby
|
|
244
|
+
# Create
|
|
245
|
+
pet = client.create_pet(
|
|
246
|
+
name: "Max",
|
|
247
|
+
photo_urls: ["https://example.com/max.jpg"],
|
|
248
|
+
category: { id: 1, name: "Dogs" },
|
|
249
|
+
tags: [{ id: 1, name: "friendly" }],
|
|
250
|
+
status: "available" # available | pending | sold
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
# Read
|
|
254
|
+
pet = client.get_pet(123)
|
|
255
|
+
|
|
256
|
+
# Update
|
|
257
|
+
updated = client.update_pet(
|
|
258
|
+
id: 123,
|
|
259
|
+
name: "Max Updated",
|
|
260
|
+
photo_urls: ["https://example.com/max-new.jpg"],
|
|
261
|
+
status: "sold"
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
# Delete
|
|
265
|
+
client.delete_pet(123)
|
|
266
|
+
|
|
267
|
+
# Find by status (with pagination)
|
|
268
|
+
pets = client.pets.find_by_status("available", page: 1, per_page: 10)
|
|
269
|
+
|
|
270
|
+
# Find by tags
|
|
271
|
+
pets = client.pets.find_by_tags(["friendly", "vaccinated"])
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
### ๐ Store Orders
|
|
275
|
+
|
|
276
|
+
```ruby
|
|
277
|
+
# Create order
|
|
278
|
+
order = client.create_order(
|
|
279
|
+
pet_id: 123,
|
|
280
|
+
quantity: 2,
|
|
281
|
+
status: "placed", # placed | approved | delivered
|
|
282
|
+
ship_date: DateTime.now + 7
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
# Get order
|
|
286
|
+
order = client.get_order(987)
|
|
287
|
+
|
|
288
|
+
# Delete order
|
|
289
|
+
client.delete_order(987)
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
### ๐ Pagination
|
|
293
|
+
|
|
294
|
+
```ruby
|
|
295
|
+
pets = client.pets.find_by_status("available", page: 1, per_page: 25)
|
|
296
|
+
|
|
297
|
+
# Navigation
|
|
298
|
+
puts "Page #{pets.page} of #{pets.total_pages}"
|
|
299
|
+
puts "Showing #{pets.count} of #{pets.total_count}"
|
|
300
|
+
|
|
301
|
+
pets.next_page? # => true
|
|
302
|
+
pets.prev_page? # => false
|
|
303
|
+
pets.first_page? # => true
|
|
304
|
+
pets.last_page? # => false
|
|
305
|
+
|
|
306
|
+
# Iterate
|
|
307
|
+
pets.each { |pet| puts pet.name }
|
|
308
|
+
pets.map(&:id)
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
## ๐ก๏ธ Error Handling
|
|
312
|
+
|
|
313
|
+
```mermaid
|
|
314
|
+
graph TD
|
|
315
|
+
A[PetstoreApiClient::Error] --> B[ValidationError<br/>โ ๏ธ Pre-request]
|
|
316
|
+
A --> C[ConfigurationError<br/>โ๏ธ Config invalid]
|
|
317
|
+
A --> D[AuthenticationError<br/>๐ Auth failed]
|
|
318
|
+
A --> E[NotFoundError<br/>โ 404]
|
|
319
|
+
A --> F[InvalidInputError<br/>โ ๏ธ 400/405]
|
|
320
|
+
A --> G[InvalidOrderError<br/>๐ฆ 400 order]
|
|
321
|
+
A --> H[RateLimitError<br/>โฑ๏ธ 429]
|
|
322
|
+
A --> I[ConnectionError<br/>๐ Network]
|
|
323
|
+
A --> J[ApiError<br/>๐ง 5xx]
|
|
324
|
+
|
|
325
|
+
style A fill:#ffebee
|
|
326
|
+
style D fill:#fff3e0
|
|
327
|
+
style H fill:#fff9c4
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
```ruby
|
|
331
|
+
begin
|
|
332
|
+
pet = client.get_pet(999999)
|
|
333
|
+
rescue PetstoreApiClient::NotFoundError => e
|
|
334
|
+
puts "Not found: #{e.message}"
|
|
335
|
+
rescue PetstoreApiClient::AuthenticationError => e
|
|
336
|
+
puts "Auth failed: #{e.message}"
|
|
337
|
+
rescue PetstoreApiClient::ValidationError => e
|
|
338
|
+
puts "Validation: #{e.message}"
|
|
339
|
+
rescue PetstoreApiClient::ApiError => e
|
|
340
|
+
puts "API error: #{e.message} (#{e.status_code})"
|
|
341
|
+
end
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
## ๐๏ธ Architecture
|
|
345
|
+
|
|
346
|
+
```mermaid
|
|
347
|
+
graph TB
|
|
348
|
+
A[Your App] --> B[ApiClient]
|
|
349
|
+
B --> C[PetClient]
|
|
350
|
+
B --> D[StoreClient]
|
|
351
|
+
|
|
352
|
+
C --> E[Request Module]
|
|
353
|
+
D --> E
|
|
354
|
+
|
|
355
|
+
E --> F[Connection]
|
|
356
|
+
F --> G[Middleware Stack]
|
|
357
|
+
|
|
358
|
+
G --> H[AuthMiddleware<br/>๐ Add auth headers]
|
|
359
|
+
H --> I[RetryMiddleware<br/>๐ Auto-retry]
|
|
360
|
+
I --> J[RateLimitMiddleware<br/>โฑ๏ธ Handle 429]
|
|
361
|
+
J --> K[JSON Parser]
|
|
362
|
+
K --> L[Petstore API]
|
|
363
|
+
|
|
364
|
+
M[Configuration] -.-> B
|
|
365
|
+
M -.-> F
|
|
366
|
+
|
|
367
|
+
N[Authentication<br/>Strategy] --> O[ApiKey]
|
|
368
|
+
N --> P[OAuth2]
|
|
369
|
+
N --> Q[Composite]
|
|
370
|
+
N --> R[None]
|
|
371
|
+
|
|
372
|
+
H -.uses.-> N
|
|
373
|
+
|
|
374
|
+
style B fill:#e1f5ff
|
|
375
|
+
style C fill:#fff3e0
|
|
376
|
+
style D fill:#fff3e0
|
|
377
|
+
style H fill:#c8e6c9
|
|
378
|
+
style I fill:#fff9c4
|
|
379
|
+
style J fill:#ffcdd2
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
## ๐งช Testing
|
|
383
|
+
|
|
384
|
+
| Metric | Value |
|
|
385
|
+
|------------------------|-------------|
|
|
386
|
+
| โ
**Total Tests** | 454 passing |
|
|
387
|
+
| ๐ **Line Coverage** | 96.91% |
|
|
388
|
+
| ๐ **Branch Coverage** | 86.21% |
|
|
389
|
+
| ๐ฏ **RuboCop** | 0 offenses |
|
|
390
|
+
|
|
391
|
+
### ๐ Quick Test (From Project Root)
|
|
392
|
+
|
|
393
|
+
```bash
|
|
394
|
+
# One-command test
|
|
395
|
+
./bin/test
|
|
396
|
+
|
|
397
|
+
# Or manually
|
|
398
|
+
bundle install
|
|
399
|
+
bundle exec rspec
|
|
400
|
+
|
|
401
|
+
# With detailed output
|
|
402
|
+
bundle exec rspec --format documentation
|
|
403
|
+
|
|
404
|
+
# Lint check
|
|
405
|
+
bundle exec rubocop
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
### ๐ Coverage Report
|
|
409
|
+
|
|
410
|
+
```bash
|
|
411
|
+
bundle exec rspec
|
|
412
|
+
open coverage/index.html # Mac
|
|
413
|
+
xdg-open coverage/index.html # Linux
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
### ๐ฎ Interactive Console
|
|
417
|
+
|
|
418
|
+
**IRB Console (Pre-configured):**
|
|
419
|
+
```bash
|
|
420
|
+
bin/console
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
The console automatically loads the gem and creates a `client` instance:
|
|
424
|
+
|
|
425
|
+
```ruby
|
|
426
|
+
# Client is ready to use!
|
|
427
|
+
pet = client.create_pet(
|
|
428
|
+
name: "TestDog",
|
|
429
|
+
photo_urls: ["http://example.com/dog.jpg"],
|
|
430
|
+
status: "available"
|
|
431
|
+
)
|
|
432
|
+
puts "Created: #{pet.name} (ID: #{pet.id})"
|
|
433
|
+
|
|
434
|
+
# Test OAuth2 authentication
|
|
435
|
+
client.configure do |config|
|
|
436
|
+
config.auth_strategy = :oauth2
|
|
437
|
+
config.oauth2_client_id = "test-client"
|
|
438
|
+
config.oauth2_client_secret = "test-secret"
|
|
439
|
+
end
|
|
440
|
+
|
|
441
|
+
# Clean up
|
|
442
|
+
client.delete_pet(pet.id)
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
### ๐ Rails Console Integration
|
|
446
|
+
|
|
447
|
+
**Option 1: Gemfile Installation**
|
|
448
|
+
|
|
449
|
+
Add to your Rails `Gemfile`:
|
|
450
|
+
```ruby
|
|
451
|
+
gem 'petstore_api_client'
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
Then in Rails console:
|
|
455
|
+
```ruby
|
|
456
|
+
rails console
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
```ruby
|
|
460
|
+
# Create client with API Key
|
|
461
|
+
client = PetstoreApiClient::ApiClient.new
|
|
462
|
+
client.configure do |config|
|
|
463
|
+
config.api_key = ENV['PETSTORE_API_KEY']
|
|
464
|
+
# or
|
|
465
|
+
config.api_key = :from_env
|
|
466
|
+
end
|
|
467
|
+
|
|
468
|
+
# Test it
|
|
469
|
+
pet = client.create_pet(
|
|
470
|
+
name: "RailsPet",
|
|
471
|
+
photo_urls: ["https://example.com/rails-pet.jpg"]
|
|
472
|
+
)
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
**Option 2: Load from Local Path**
|
|
476
|
+
|
|
477
|
+
In Rails console:
|
|
478
|
+
```ruby
|
|
479
|
+
# Load from local gem directory
|
|
480
|
+
$LOAD_PATH.unshift('/path/to/petstore-api-client/lib')
|
|
481
|
+
require 'petstore_api_client'
|
|
482
|
+
|
|
483
|
+
# Use it
|
|
484
|
+
client = PetstoreApiClient::ApiClient.new
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
**Option 3: Rails Initializer**
|
|
488
|
+
|
|
489
|
+
Create `config/initializers/petstore.rb`:
|
|
490
|
+
```ruby
|
|
491
|
+
# config/initializers/petstore.rb
|
|
492
|
+
PetstoreApiClient.configure do |config|
|
|
493
|
+
config.auth_strategy = :oauth2
|
|
494
|
+
config.oauth2_client_id = ENV['PETSTORE_OAUTH2_CLIENT_ID']
|
|
495
|
+
config.oauth2_client_secret = ENV['PETSTORE_OAUTH2_CLIENT_SECRET']
|
|
496
|
+
config.timeout = 60
|
|
497
|
+
end
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
Then in your Rails app:
|
|
501
|
+
```ruby
|
|
502
|
+
# app/services/pet_service.rb
|
|
503
|
+
class PetService
|
|
504
|
+
def self.create_pet(name:, photo_urls:)
|
|
505
|
+
client = PetstoreApiClient::ApiClient.new
|
|
506
|
+
client.create_pet(
|
|
507
|
+
name: name,
|
|
508
|
+
photo_urls: photo_urls,
|
|
509
|
+
status: 'available'
|
|
510
|
+
)
|
|
511
|
+
rescue PetstoreApiClient::ValidationError => e
|
|
512
|
+
Rails.logger.error("Validation failed: #{e.message}")
|
|
513
|
+
nil
|
|
514
|
+
end
|
|
515
|
+
end
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
### ๐ Environment Setup
|
|
519
|
+
|
|
520
|
+
**1. Copy environment template:**
|
|
521
|
+
```bash
|
|
522
|
+
cp .env.example .env
|
|
523
|
+
```
|
|
524
|
+
|
|
525
|
+
**2. Edit `.env` with your credentials:**
|
|
526
|
+
```bash
|
|
527
|
+
# Choose your auth strategy
|
|
528
|
+
PETSTORE_API_KEY=special-key
|
|
529
|
+
|
|
530
|
+
# OR for OAuth2
|
|
531
|
+
PETSTORE_OAUTH2_CLIENT_ID=my-client-id
|
|
532
|
+
PETSTORE_OAUTH2_CLIENT_SECRET=my-secret
|
|
533
|
+
```
|
|
534
|
+
|
|
535
|
+
**3. Load in Rails:**
|
|
536
|
+
```ruby
|
|
537
|
+
# Gemfile
|
|
538
|
+
gem 'dotenv-rails', groups: [:development, :test]
|
|
539
|
+
|
|
540
|
+
# .env is automatically loaded
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
### โ ๏ธ Security Checklist
|
|
544
|
+
|
|
545
|
+
Before committing:
|
|
546
|
+
```bash
|
|
547
|
+
# 1. Check .gitignore includes sensitive files
|
|
548
|
+
cat .gitignore | grep -E '\.env|credentials|secrets|\.pem|\.key'
|
|
549
|
+
|
|
550
|
+
# 2. Verify no secrets in git
|
|
551
|
+
git status
|
|
552
|
+
git diff
|
|
553
|
+
|
|
554
|
+
# 3. Check for hardcoded secrets
|
|
555
|
+
grep -r "client_secret\|api_key" lib/ --exclude-dir=spec
|
|
556
|
+
|
|
557
|
+
# 4. Ensure .env is not staged
|
|
558
|
+
git ls-files | grep "\.env$" && echo "โ ๏ธ WARNING: .env is tracked!"
|
|
559
|
+
```
|
|
560
|
+
|
|
561
|
+
**Never commit:**
|
|
562
|
+
- โ `.env` files
|
|
563
|
+
- โ `credentials.json`
|
|
564
|
+
- โ `*.pem`, `*.key` files
|
|
565
|
+
- โ OAuth2 client secrets
|
|
566
|
+
- โ API keys in code
|
|
567
|
+
|
|
568
|
+
### ๐ CI/CD Pipeline
|
|
569
|
+
|
|
570
|
+
GitHub Actions automatically runs on push/PR:
|
|
571
|
+
|
|
572
|
+
| Step | Command | Purpose |
|
|
573
|
+
|-------------|-----------------------|----------------------------|
|
|
574
|
+
| ๐งช Tests | `bundle exec rspec` | Run 454 tests |
|
|
575
|
+
| ๐ Lint | `bundle exec rubocop` | Code quality |
|
|
576
|
+
| ๐ Security | `bundle audit` | Dependency vulnerabilities |
|
|
577
|
+
| ๐ฆ Build | `gem build` | Build gem package |
|
|
578
|
+
| ๐ Coverage | Check 95%+ threshold | Ensure quality |
|
|
579
|
+
|
|
580
|
+
**View CI status:**
|
|
581
|
+
```
|
|
582
|
+
https://github.com/hammadxcm/petstore-api-client/actions
|
|
583
|
+
```
|
|
584
|
+
|
|
585
|
+
**CI/CD Badge:**
|
|
586
|
+
```markdown
|
|
587
|
+
[](https://github.com/hammadxcm/petstore-api-client/actions)
|
|
588
|
+
```
|
|
589
|
+
|
|
590
|
+
## ๐ API Coverage
|
|
591
|
+
|
|
592
|
+
| Endpoint | Method | Client Method |
|
|
593
|
+
|---------------------|--------|--------------------------------|
|
|
594
|
+
| `/pet` | POST | `create_pet(data)` |
|
|
595
|
+
| `/pet` | PUT | `update_pet(data)` |
|
|
596
|
+
| `/pet/{id}` | GET | `get_pet(id)` |
|
|
597
|
+
| `/pet/{id}` | DELETE | `delete_pet(id)` |
|
|
598
|
+
| `/pet/findByStatus` | GET | `find_by_status(status, opts)` |
|
|
599
|
+
| `/pet/findByTags` | GET | `find_by_tags(tags, opts)` |
|
|
600
|
+
| `/store/order` | POST | `create_order(data)` |
|
|
601
|
+
| `/store/order/{id}` | GET | `get_order(id)` |
|
|
602
|
+
| `/store/order/{id}` | DELETE | `delete_order(id)` |
|
|
603
|
+
|
|
604
|
+
## ๐ Documentation
|
|
605
|
+
|
|
606
|
+
- ๐ง [YARD Docs](https://rubydoc.info/gems/petstore_api_client) - Full API reference
|
|
607
|
+
- ๐ Authentication guide is included above (see Authentication section)
|
|
608
|
+
- ๐ฉ Feature flags documented above (see Auth Strategies)
|
|
609
|
+
|
|
610
|
+
## ๐๏ธ Design Principles
|
|
611
|
+
|
|
612
|
+
โ
**SOLID** - Single Responsibility, Open/Closed, Liskov, Interface Segregation, Dependency Inversion
|
|
613
|
+
โ
**Strategy Pattern** - Swappable authentication strategies
|
|
614
|
+
โ
**Middleware Pattern** - Composable Faraday middleware
|
|
615
|
+
โ
**Factory Pattern** - Configuration builds authenticators
|
|
616
|
+
โ
**Composite Pattern** - Combine multiple auth strategies
|
|
617
|
+
โ
**Null Object** - None authenticator for consistent interface
|
|
618
|
+
|
|
619
|
+
## ๐ฆ Dependencies
|
|
620
|
+
|
|
621
|
+
**Runtime:**
|
|
622
|
+
- `faraday` (~> 2.0) - HTTP client
|
|
623
|
+
- `faraday-retry` (~> 2.0) - Auto-retry middleware
|
|
624
|
+
- `oauth2` (~> 2.0) - OAuth2 client
|
|
625
|
+
- `activemodel` (>= 6.0) - Validations
|
|
626
|
+
|
|
627
|
+
**Development:**
|
|
628
|
+
- `rspec` (~> 3.12) - Testing
|
|
629
|
+
- `vcr` (~> 6.0) - HTTP recording
|
|
630
|
+
- `simplecov` (~> 0.22) - Coverage
|
|
631
|
+
|
|
632
|
+
## ๐ค Contributing
|
|
633
|
+
|
|
634
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
635
|
+
|
|
636
|
+
**How to contribute:**
|
|
637
|
+
|
|
638
|
+
1. ๐ด Fork it ([https://github.com/hammadxcm/petstore-api-client/fork](https://github.com/hammadxcm/petstore-api-client/fork))
|
|
639
|
+
2. ๐ฟ Create feature branch (`git checkout -b feature/amazing-feature`)
|
|
640
|
+
3. โ
Add tests for your changes
|
|
641
|
+
4. ๐งช Run tests (`bundle exec rspec`)
|
|
642
|
+
5. ๐ Run linter (`bundle exec rubocop`)
|
|
643
|
+
6. ๐พ Commit (`git commit -m 'Add amazing feature'`)
|
|
644
|
+
7. ๐ค Push (`git push origin feature/amazing-feature`)
|
|
645
|
+
8. ๐ Create Pull Request
|
|
646
|
+
|
|
647
|
+
**Code owners:** Changes will be automatically reviewed by [@hammadxcm](https://github.com/hammadxcm)
|
|
648
|
+
|
|
649
|
+
**Guidelines:**
|
|
650
|
+
- Write tests for new features
|
|
651
|
+
- Follow existing code style
|
|
652
|
+
- Update documentation
|
|
653
|
+
- Keep commits focused and atomic
|
|
654
|
+
|
|
655
|
+
## ๐ License
|
|
656
|
+
|
|
657
|
+
MIT License - see [LICENSE](LICENSE)
|
|
658
|
+
|
|
659
|
+
---
|
|
660
|
+
|
|
661
|
+
## ๐ฌ Support & Contact
|
|
662
|
+
|
|
663
|
+
- ๐ค **Author:** Hammad Khan ([@hammadxcm](https://github.com/hammadxcm))
|
|
664
|
+
- ๐ **Issues:** [GitHub Issues](https://github.com/hammadxcm/petstore-api-client/issues)
|
|
665
|
+
- ๐ก **Feature Requests:** [GitHub Discussions](https://github.com/hammadxcm/petstore-api-client/discussions)
|
|
666
|
+
- ๐ง **Contact:** [Open an issue](https://github.com/hammadxcm/petstore-api-client/issues/new)
|
|
667
|
+
- โญ **Star the repo:** [github.com/hammadxcm/petstore-api-client](https://github.com/hammadxcm/petstore-api-client)
|
|
668
|
+
|
|
669
|
+
---
|
|
670
|
+
|
|
671
|
+
<div align="center">
|
|
672
|
+
|
|
673
|
+
**๐พ Made with โค๏ธ for the Ruby community by [@hammadxcm](https://github.com/hammadxcm)**
|
|
674
|
+
|
|
675
|
+
[](https://www.ruby-lang.org/)
|
|
676
|
+
[](https://oauth.net/2/)
|
|
677
|
+
[](LICENSE)
|
|
678
|
+
[](https://github.com/hammadxcm)
|
|
679
|
+
|
|
680
|
+
[Quick Start](#-quick-start) โข [Authentication](#-authentication) โข [Examples](#-usage-examples) โข [Contributing](#-contributing) โข [Issues](https://github.com/hammadxcm/petstore-api-client/issues)
|
|
681
|
+
|
|
682
|
+
**Repository:** [github.com/hammadxcm/petstore-api-client](https://github.com/hammadxcm/petstore-api-client)
|
|
683
|
+
|
|
684
|
+
</div>
|