sirv_rest_api 1.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.
data/docs/index.html ADDED
@@ -0,0 +1,539 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Sirv REST API Ruby SDK Documentation</title>
7
+ <style>
8
+ :root {
9
+ --primary-color: #cc342d;
10
+ --secondary-color: #a12a24;
11
+ --bg-color: #f8f9fa;
12
+ --text-color: #333;
13
+ --code-bg: #f4f4f4;
14
+ --border-color: #e1e4e8;
15
+ }
16
+ * {
17
+ box-sizing: border-box;
18
+ margin: 0;
19
+ padding: 0;
20
+ }
21
+ body {
22
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
23
+ line-height: 1.6;
24
+ color: var(--text-color);
25
+ background-color: var(--bg-color);
26
+ }
27
+ .container {
28
+ max-width: 1200px;
29
+ margin: 0 auto;
30
+ padding: 20px;
31
+ }
32
+ header {
33
+ background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
34
+ color: white;
35
+ padding: 40px 0;
36
+ margin-bottom: 30px;
37
+ }
38
+ header h1 {
39
+ font-size: 2.5rem;
40
+ margin-bottom: 10px;
41
+ }
42
+ header p {
43
+ font-size: 1.2rem;
44
+ opacity: 0.9;
45
+ }
46
+ .badge {
47
+ display: inline-block;
48
+ background: rgba(255,255,255,0.2);
49
+ padding: 5px 15px;
50
+ border-radius: 20px;
51
+ margin: 10px 5px 0 0;
52
+ font-size: 0.9rem;
53
+ }
54
+ nav {
55
+ background: white;
56
+ border-radius: 8px;
57
+ padding: 20px;
58
+ margin-bottom: 30px;
59
+ box-shadow: 0 2px 10px rgba(0,0,0,0.05);
60
+ }
61
+ nav h2 {
62
+ margin-bottom: 15px;
63
+ color: var(--secondary-color);
64
+ }
65
+ nav ul {
66
+ list-style: none;
67
+ display: grid;
68
+ grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
69
+ gap: 10px;
70
+ }
71
+ nav a {
72
+ color: var(--primary-color);
73
+ text-decoration: none;
74
+ padding: 8px 12px;
75
+ display: block;
76
+ border-radius: 4px;
77
+ transition: background 0.2s;
78
+ }
79
+ nav a:hover {
80
+ background: var(--bg-color);
81
+ }
82
+ section {
83
+ background: white;
84
+ border-radius: 8px;
85
+ padding: 30px;
86
+ margin-bottom: 30px;
87
+ box-shadow: 0 2px 10px rgba(0,0,0,0.05);
88
+ }
89
+ h2 {
90
+ color: var(--secondary-color);
91
+ margin-bottom: 20px;
92
+ padding-bottom: 10px;
93
+ border-bottom: 2px solid var(--border-color);
94
+ }
95
+ h3 {
96
+ color: var(--text-color);
97
+ margin: 25px 0 15px;
98
+ }
99
+ pre {
100
+ background: var(--code-bg);
101
+ padding: 20px;
102
+ border-radius: 8px;
103
+ overflow-x: auto;
104
+ margin: 15px 0;
105
+ border: 1px solid var(--border-color);
106
+ }
107
+ code {
108
+ font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
109
+ font-size: 0.9rem;
110
+ }
111
+ .inline-code {
112
+ background: var(--code-bg);
113
+ padding: 2px 6px;
114
+ border-radius: 4px;
115
+ font-size: 0.9em;
116
+ }
117
+ table {
118
+ width: 100%;
119
+ border-collapse: collapse;
120
+ margin: 15px 0;
121
+ }
122
+ th, td {
123
+ padding: 12px;
124
+ text-align: left;
125
+ border-bottom: 1px solid var(--border-color);
126
+ }
127
+ th {
128
+ background: var(--bg-color);
129
+ font-weight: 600;
130
+ }
131
+ .method-signature {
132
+ background: #fef0f0;
133
+ padding: 15px;
134
+ border-radius: 8px;
135
+ margin: 10px 0;
136
+ border-left: 4px solid var(--primary-color);
137
+ }
138
+ .note {
139
+ background: #fff3cd;
140
+ padding: 15px;
141
+ border-radius: 8px;
142
+ margin: 15px 0;
143
+ border-left: 4px solid #ffc107;
144
+ }
145
+ footer {
146
+ text-align: center;
147
+ padding: 30px;
148
+ color: #666;
149
+ }
150
+ footer a {
151
+ color: var(--primary-color);
152
+ }
153
+ .keyword { color: #d73a49; }
154
+ .string { color: #032f62; }
155
+ .comment { color: #6a737d; }
156
+ .symbol { color: #e36209; }
157
+ .method { color: #6f42c1; }
158
+ </style>
159
+ </head>
160
+ <body>
161
+ <header>
162
+ <div class="container">
163
+ <h1>Sirv REST API Ruby SDK</h1>
164
+ <p>Official Ruby SDK for the Sirv image hosting and processing platform</p>
165
+ <span class="badge">Ruby 2.7+</span>
166
+ <span class="badge">40+ API endpoints</span>
167
+ <span class="badge">MIT License</span>
168
+ </div>
169
+ </header>
170
+
171
+ <div class="container">
172
+ <nav>
173
+ <h2>Quick Navigation</h2>
174
+ <ul>
175
+ <li><a href="#installation">Installation</a></li>
176
+ <li><a href="#quick-start">Quick Start</a></li>
177
+ <li><a href="#configuration">Configuration</a></li>
178
+ <li><a href="#authentication">Authentication</a></li>
179
+ <li><a href="#account">Account API</a></li>
180
+ <li><a href="#files">Files API</a></li>
181
+ <li><a href="#metadata">Metadata API</a></li>
182
+ <li><a href="#jwt">JWT API</a></li>
183
+ <li><a href="#spins">Spins/360 API</a></li>
184
+ <li><a href="#statistics">Statistics API</a></li>
185
+ <li><a href="#errors">Error Handling</a></li>
186
+ </ul>
187
+ </nav>
188
+
189
+ <section id="installation">
190
+ <h2>Installation</h2>
191
+ <pre><code>gem install sirv_rest_api</code></pre>
192
+ <p>Or add to your Gemfile:</p>
193
+ <pre><code>gem <span class="string">'sirv_rest_api'</span></code></pre>
194
+ <p>Requirements: Ruby 2.7 or later</p>
195
+ </section>
196
+
197
+ <section id="quick-start">
198
+ <h2>Quick Start</h2>
199
+ <pre><code><span class="keyword">require</span> <span class="string">'sirv_rest_api'</span>
200
+
201
+ <span class="comment"># Create client</span>
202
+ client = SirvRestApi::Client.<span class="method">new</span>(
203
+ <span class="symbol">client_id:</span> <span class="string">'your-client-id'</span>,
204
+ <span class="symbol">client_secret:</span> <span class="string">'your-client-secret'</span>
205
+ )
206
+
207
+ <span class="comment"># Connect (authenticate)</span>
208
+ token = client.<span class="method">connect</span>
209
+ puts <span class="string">"Connected! Token expires in #{token.expires_in} seconds"</span>
210
+
211
+ <span class="comment"># Get account info</span>
212
+ account = client.<span class="method">get_account_info</span>
213
+ puts <span class="string">"Account: #{account.alias}"</span>
214
+ puts <span class="string">"CDN URL: #{account.cdn_url}"</span></code></pre>
215
+ </section>
216
+
217
+ <section id="configuration">
218
+ <h2>Configuration</h2>
219
+ <pre><code>client = SirvRestApi::Client.<span class="method">new</span>(
220
+ <span class="comment"># Required</span>
221
+ <span class="symbol">client_id:</span> <span class="string">'your-client-id'</span>,
222
+ <span class="symbol">client_secret:</span> <span class="string">'your-client-secret'</span>,
223
+
224
+ <span class="comment"># Optional</span>
225
+ <span class="symbol">base_url:</span> <span class="string">'https://api.sirv.com'</span>, <span class="comment"># Default</span>
226
+ <span class="symbol">auto_refresh_token:</span> <span class="keyword">true</span>, <span class="comment"># Default: true</span>
227
+ <span class="symbol">token_refresh_buffer:</span> 60, <span class="comment"># Seconds before expiry to refresh</span>
228
+ <span class="symbol">timeout:</span> 30, <span class="comment"># Request timeout in seconds</span>
229
+ <span class="symbol">max_retries:</span> 3 <span class="comment"># Max retries on 5xx errors</span>
230
+ )</code></pre>
231
+
232
+ <h3>Configuration Options</h3>
233
+ <table>
234
+ <tr>
235
+ <th>Option</th>
236
+ <th>Type</th>
237
+ <th>Default</th>
238
+ <th>Description</th>
239
+ </tr>
240
+ <tr>
241
+ <td><code class="inline-code">client_id</code></td>
242
+ <td>String</td>
243
+ <td>(required)</td>
244
+ <td>Your Sirv API client ID</td>
245
+ </tr>
246
+ <tr>
247
+ <td><code class="inline-code">client_secret</code></td>
248
+ <td>String</td>
249
+ <td>(required)</td>
250
+ <td>Your Sirv API client secret</td>
251
+ </tr>
252
+ <tr>
253
+ <td><code class="inline-code">base_url</code></td>
254
+ <td>String</td>
255
+ <td>https://api.sirv.com</td>
256
+ <td>Base URL for API requests</td>
257
+ </tr>
258
+ <tr>
259
+ <td><code class="inline-code">auto_refresh_token</code></td>
260
+ <td>Boolean</td>
261
+ <td>true</td>
262
+ <td>Automatically refresh token before expiry</td>
263
+ </tr>
264
+ <tr>
265
+ <td><code class="inline-code">token_refresh_buffer</code></td>
266
+ <td>Integer</td>
267
+ <td>60</td>
268
+ <td>Seconds before expiry to trigger refresh</td>
269
+ </tr>
270
+ <tr>
271
+ <td><code class="inline-code">timeout</code></td>
272
+ <td>Integer</td>
273
+ <td>30</td>
274
+ <td>Request timeout in seconds</td>
275
+ </tr>
276
+ <tr>
277
+ <td><code class="inline-code">max_retries</code></td>
278
+ <td>Integer</td>
279
+ <td>3</td>
280
+ <td>Maximum retries for 5xx errors</td>
281
+ </tr>
282
+ </table>
283
+ </section>
284
+
285
+ <section id="authentication">
286
+ <h2>Authentication</h2>
287
+
288
+ <h3>connect</h3>
289
+ <div class="method-signature">
290
+ <code>client.connect(expires_in: nil) → TokenResponse</code>
291
+ </div>
292
+ <p>Authenticates and obtains a bearer token. The <code class="inline-code">expires_in</code> parameter is optional and specifies token lifetime in seconds (5-604800).</p>
293
+ <pre><code><span class="comment"># Default token (20 minutes)</span>
294
+ token = client.<span class="method">connect</span>
295
+
296
+ <span class="comment"># Custom expiry (1 hour)</span>
297
+ token = client.<span class="method">connect</span>(<span class="symbol">expires_in:</span> 3600)
298
+
299
+ <span class="comment"># Custom expiry (7 days)</span>
300
+ token = client.<span class="method">connect</span>(<span class="symbol">expires_in:</span> 604800)</code></pre>
301
+
302
+ <h3>connected?</h3>
303
+ <div class="method-signature">
304
+ <code>client.connected? → Boolean</code>
305
+ </div>
306
+ <p>Checks if client has a valid token.</p>
307
+
308
+ <h3>access_token</h3>
309
+ <div class="method-signature">
310
+ <code>client.access_token → String or nil</code>
311
+ </div>
312
+ <p>Returns the current token (useful for external integrations).</p>
313
+ </section>
314
+
315
+ <section id="account">
316
+ <h2>Account API</h2>
317
+
318
+ <h3>get_account_info</h3>
319
+ <div class="method-signature">
320
+ <code>client.get_account_info → AccountInfo</code>
321
+ </div>
322
+
323
+ <h3>update_account</h3>
324
+ <div class="method-signature">
325
+ <code>client.update_account(options) → nil</code>
326
+ </div>
327
+
328
+ <h3>get_account_limits</h3>
329
+ <div class="method-signature">
330
+ <code>client.get_account_limits → AccountLimits</code>
331
+ </div>
332
+
333
+ <h3>get_storage_info</h3>
334
+ <div class="method-signature">
335
+ <code>client.get_storage_info → StorageInfo</code>
336
+ </div>
337
+
338
+ <h3>get_billing_plan</h3>
339
+ <div class="method-signature">
340
+ <code>client.get_billing_plan → BillingPlan</code>
341
+ </div>
342
+
343
+ <h3>get_account_users</h3>
344
+ <div class="method-signature">
345
+ <code>client.get_account_users → Array&lt;AccountUser&gt;</code>
346
+ </div>
347
+
348
+ <h3>search_events</h3>
349
+ <div class="method-signature">
350
+ <code>client.search_events(params = {}) → Array&lt;AccountEvent&gt;</code>
351
+ </div>
352
+ </section>
353
+
354
+ <section id="files">
355
+ <h2>Files API</h2>
356
+
357
+ <h3>Reading Files</h3>
358
+ <div class="method-signature">
359
+ <code>client.get_file_info(filename) → FileInfo</code>
360
+ </div>
361
+ <div class="method-signature">
362
+ <code>client.read_folder_contents(dirname, continuation: nil) → FolderContents</code>
363
+ </div>
364
+ <div class="method-signature">
365
+ <code>client.each_folder_item(dirname) { |file| ... }</code>
366
+ </div>
367
+ <div class="method-signature">
368
+ <code>client.search_files(params = {}) → SearchResult</code>
369
+ </div>
370
+ <div class="method-signature">
371
+ <code>client.each_search_result(params = {}) { |file| ... }</code>
372
+ </div>
373
+ <div class="method-signature">
374
+ <code>client.download_file(filename) → String</code>
375
+ </div>
376
+ <div class="method-signature">
377
+ <code>client.download_file_to(filename, local_path) → nil</code>
378
+ </div>
379
+
380
+ <h3>Writing Files</h3>
381
+ <div class="method-signature">
382
+ <code>client.upload_file(target_path, content, content_type: nil) → nil</code>
383
+ </div>
384
+ <div class="method-signature">
385
+ <code>client.upload_file_from_path(target_path, local_path, content_type: nil) → nil</code>
386
+ </div>
387
+ <div class="method-signature">
388
+ <code>client.create_folder(dirname) → nil</code>
389
+ </div>
390
+ <div class="method-signature">
391
+ <code>client.delete_file(filename) → nil</code>
392
+ </div>
393
+ <div class="method-signature">
394
+ <code>client.copy_file(from:, to:) → nil</code>
395
+ </div>
396
+ <div class="method-signature">
397
+ <code>client.rename_file(from:, to:) → nil</code>
398
+ </div>
399
+ <div class="method-signature">
400
+ <code>client.fetch_url(url:, filename:, wait: nil) → nil</code>
401
+ </div>
402
+ </section>
403
+
404
+ <section id="metadata">
405
+ <h2>Metadata API</h2>
406
+ <div class="method-signature">
407
+ <code>client.get_file_meta(filename) → FileMeta</code>
408
+ </div>
409
+ <div class="method-signature">
410
+ <code>client.set_file_meta(filename, meta) → nil</code>
411
+ </div>
412
+ <div class="method-signature">
413
+ <code>client.get_file_title(filename) → String</code>
414
+ </div>
415
+ <div class="method-signature">
416
+ <code>client.set_file_title(filename, title) → nil</code>
417
+ </div>
418
+ <div class="method-signature">
419
+ <code>client.get_file_tags(filename) → Array&lt;String&gt;</code>
420
+ </div>
421
+ <div class="method-signature">
422
+ <code>client.add_file_tags(filename, tags) → nil</code>
423
+ </div>
424
+ <div class="method-signature">
425
+ <code>client.remove_file_tags(filename, tags) → nil</code>
426
+ </div>
427
+ </section>
428
+
429
+ <section id="jwt">
430
+ <h2>JWT API</h2>
431
+ <div class="method-signature">
432
+ <code>client.generate_jwt(filename:, expires_in: nil, secure_params: nil) → JwtResponse</code>
433
+ </div>
434
+ <pre><code>jwt = client.<span class="method">generate_jwt</span>(
435
+ <span class="symbol">filename:</span> <span class="string">'/protected/image.jpg'</span>,
436
+ <span class="symbol">expires_in:</span> 3600 <span class="comment"># 1 hour</span>
437
+ )
438
+ puts <span class="string">"Protected URL: #{jwt.url}"</span></code></pre>
439
+ </section>
440
+
441
+ <section id="spins">
442
+ <h2>Spins/360 API</h2>
443
+ <div class="method-signature">
444
+ <code>client.spin_to_video(filename, options: nil) → String</code>
445
+ </div>
446
+ <div class="method-signature">
447
+ <code>client.video_to_spin(filename, target_filename: nil, options: nil) → String</code>
448
+ </div>
449
+ <div class="method-signature">
450
+ <code>client.export_spin_to_amazon(filename:, asin: nil, product_id: nil) → nil</code>
451
+ </div>
452
+ <div class="method-signature">
453
+ <code>client.export_spin_to_walmart(filename:, product_id: nil) → nil</code>
454
+ </div>
455
+ <div class="method-signature">
456
+ <code>client.export_spin_to_home_depot(filename:, product_id: nil) → nil</code>
457
+ </div>
458
+ <div class="method-signature">
459
+ <code>client.export_spin_to_lowes(filename:, product_id: nil) → nil</code>
460
+ </div>
461
+ <div class="method-signature">
462
+ <code>client.export_spin_to_grainger(filename:, product_id: nil) → nil</code>
463
+ </div>
464
+ </section>
465
+
466
+ <section id="statistics">
467
+ <h2>Statistics API</h2>
468
+ <div class="method-signature">
469
+ <code>client.get_http_stats(from:, to:) → Array&lt;HttpStats&gt;</code>
470
+ </div>
471
+ <div class="method-signature">
472
+ <code>client.get_spin_views_stats(from:, to:) → Array&lt;SpinViewStats&gt;</code>
473
+ </div>
474
+ <div class="method-signature">
475
+ <code>client.get_storage_stats(from:, to:) → Array&lt;StorageStats&gt;</code>
476
+ </div>
477
+ <div class="note">
478
+ <strong>Note:</strong> Spin views statistics API has a maximum date range of 5 days.
479
+ </div>
480
+ </section>
481
+
482
+ <section id="errors">
483
+ <h2>Error Handling</h2>
484
+ <pre><code><span class="keyword">begin</span>
485
+ account = client.<span class="method">get_account_info</span>
486
+ <span class="keyword">rescue</span> SirvRestApi::AuthenticationError => e
487
+ puts <span class="string">"Authentication failed: #{e.message}"</span>
488
+ <span class="keyword">rescue</span> SirvRestApi::NotFoundError => e
489
+ puts <span class="string">"Not found: #{e.message}"</span>
490
+ <span class="keyword">rescue</span> SirvRestApi::RateLimitError => e
491
+ puts <span class="string">"Rate limit exceeded: #{e.message}"</span>
492
+ <span class="keyword">rescue</span> SirvRestApi::ApiError => e
493
+ puts <span class="string">"API error: #{e.message} (status #{e.status_code})"</span>
494
+ <span class="keyword">end</span></code></pre>
495
+
496
+ <h3>Error Classes</h3>
497
+ <table>
498
+ <tr>
499
+ <th>Class</th>
500
+ <th>Description</th>
501
+ </tr>
502
+ <tr>
503
+ <td><code class="inline-code">SirvRestApi::Error</code></td>
504
+ <td>Base error class</td>
505
+ </tr>
506
+ <tr>
507
+ <td><code class="inline-code">SirvRestApi::ApiError</code></td>
508
+ <td>General API error with status_code and error_code</td>
509
+ </tr>
510
+ <tr>
511
+ <td><code class="inline-code">SirvRestApi::AuthenticationError</code></td>
512
+ <td>Authentication failure (401)</td>
513
+ </tr>
514
+ <tr>
515
+ <td><code class="inline-code">SirvRestApi::NotFoundError</code></td>
516
+ <td>Resource not found (404)</td>
517
+ </tr>
518
+ <tr>
519
+ <td><code class="inline-code">SirvRestApi::RateLimitError</code></td>
520
+ <td>Rate limit exceeded (429)</td>
521
+ </tr>
522
+ <tr>
523
+ <td><code class="inline-code">SirvRestApi::ValidationError</code></td>
524
+ <td>Invalid parameters (400)</td>
525
+ </tr>
526
+ </table>
527
+ </section>
528
+ </div>
529
+
530
+ <footer>
531
+ <p>
532
+ <a href="https://sirv.com">Sirv Website</a> |
533
+ <a href="https://apidocs.sirv.com/">API Documentation</a> |
534
+ <a href="https://github.com/sirv/sirv-rest-api-ruby">GitHub</a>
535
+ </p>
536
+ <p>&copy; 2025 Sirv Ltd. Licensed under MIT.</p>
537
+ </footer>
538
+ </body>
539
+ </html>
@@ -0,0 +1,68 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Example usage of the Sirv REST API Ruby SDK
5
+
6
+ require_relative "../lib/sirv_rest_api"
7
+
8
+ # Get credentials from environment variables
9
+ client_id = ENV["SIRV_CLIENT_ID"]
10
+ client_secret = ENV["SIRV_CLIENT_SECRET"]
11
+
12
+ if client_id.nil? || client_secret.nil?
13
+ puts "Please set SIRV_CLIENT_ID and SIRV_CLIENT_SECRET environment variables"
14
+ exit 1
15
+ end
16
+
17
+ # Create client
18
+ client = SirvRestApi::Client.new(
19
+ client_id: client_id,
20
+ client_secret: client_secret
21
+ )
22
+
23
+ # Connect (authenticate)
24
+ puts "Connecting to Sirv API..."
25
+ token = client.connect
26
+ puts "Connected! Token expires in #{token.expires_in} seconds"
27
+ puts "Scopes: #{token.scope.join(', ')}"
28
+ puts
29
+
30
+ # Get account info
31
+ puts "Getting account info..."
32
+ account = client.get_account_info
33
+ puts "Account: #{account.alias}"
34
+ puts "CDN URL: #{account.cdn_url}"
35
+ puts
36
+
37
+ # Get storage info
38
+ puts "Getting storage info..."
39
+ storage = client.get_storage_info
40
+ puts "Storage used: #{(storage.used / 1024.0 / 1024.0).round(2)} MB"
41
+ puts "Files: #{storage.files}"
42
+ puts
43
+
44
+ # Get billing plan
45
+ puts "Getting billing plan..."
46
+ plan = client.get_billing_plan
47
+ puts "Plan: #{plan.name}"
48
+ puts
49
+
50
+ # List root folder contents
51
+ puts "Listing root folder contents..."
52
+ folder = client.read_folder_contents("/")
53
+ folder.contents.each do |item|
54
+ type = item.directory? ? "[DIR]" : "[FILE]"
55
+ puts " #{type} #{item.filename}"
56
+ end
57
+ puts
58
+
59
+ # Search files
60
+ puts "Searching for files..."
61
+ results = client.search_files(query: "*", size: 5)
62
+ puts "Found #{results.total} total files"
63
+ results.hits.each do |hit|
64
+ puts " #{hit.filename}"
65
+ end
66
+
67
+ puts
68
+ puts "Done!"
data/icon.png ADDED
Binary file