uploadcare-ruby 5.0.0 → 5.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 +4 -4
- data/.github/workflows/gem-push.yml +3 -1
- data/CHANGELOG.md +18 -0
- data/MIGRATING_V5.md +2 -1
- data/README.md +108 -6
- data/api_examples/README.md +9 -5
- data/api_examples/rest_api/get_files_uuid_tags.rb +4 -0
- data/api_examples/rest_api/patch_files_uuid_tags.rb +4 -0
- data/api_examples/rest_api/post_files_search.rb +4 -0
- data/api_examples/rest_api/put_files_uuid_tags.rb +4 -0
- data/api_examples/support/example_helper.rb +17 -1
- data/api_examples/support/run_rest_example.rb +30 -0
- data/api_examples/support/run_upload_example.rb +8 -2
- data/context7.json +3 -1
- data/docs/release-notes-5.1.0.md +33 -0
- data/examples/README.md +4 -0
- data/examples/file_search.rb +33 -0
- data/examples/file_tags.rb +37 -0
- data/lib/uploadcare/api/rest/file_tags.rb +62 -0
- data/lib/uploadcare/api/rest/files.rb +18 -1
- data/lib/uploadcare/api/rest.rb +37 -12
- data/lib/uploadcare/api/upload/files.rb +18 -5
- data/lib/uploadcare/api/upload.rb +2 -1
- data/lib/uploadcare/client/file_tags_accessor.rb +44 -0
- data/lib/uploadcare/client/files_accessor.rb +17 -0
- data/lib/uploadcare/client.rb +7 -0
- data/lib/uploadcare/collections/file_search_result.rb +45 -0
- data/lib/uploadcare/collections/paginated.rb +7 -1
- data/lib/uploadcare/internal/file_tag_normalizer.rb +62 -0
- data/lib/uploadcare/internal/upload_params_generator.rb +16 -2
- data/lib/uploadcare/internal/user_agent.rb +1 -1
- data/lib/uploadcare/operations/file_search.rb +43 -0
- data/lib/uploadcare/operations/multipart_upload.rb +1 -1
- data/lib/uploadcare/operations/upload_router.rb +2 -2
- data/lib/uploadcare/resources/file.rb +23 -15
- data/lib/uploadcare/resources/file_tags.rb +92 -0
- data/lib/uploadcare/version.rb +1 -1
- data/lib/uploadcare.rb +2 -0
- metadata +14 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b0a3dc13f1f548e3f3083128b65c9df35524321b9ab73bff80b8eae0e6d52e0e
|
|
4
|
+
data.tar.gz: fe8b13b6f891f938aabe9d1fe39875981662c4f3f8e66b4eb234b9daf884a734
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 54cbc1ab6d88ee5f08c8cbb022d69417ffc2b064897f4db815582b3f997e143c943b178d44b3afc340b5307cb747cd7d5b7ba3a08e01b7026d99649a575cb62d
|
|
7
|
+
data.tar.gz: da4e93044e29484359810c3bcda60e720067f2add3de07f0666c7e94bda4d95c243d96c6c832b0f53f2aad62d2b902b8d2169d1e4c0e4a929514dc7eaf74152d
|
|
@@ -7,13 +7,15 @@ on:
|
|
|
7
7
|
jobs:
|
|
8
8
|
build:
|
|
9
9
|
runs-on: ubuntu-latest
|
|
10
|
+
permissions:
|
|
11
|
+
contents: write
|
|
10
12
|
|
|
11
13
|
steps:
|
|
12
14
|
- uses: actions/checkout@v5
|
|
13
15
|
|
|
14
16
|
- name: Release Gem
|
|
15
17
|
if: contains(github.ref, 'refs/tags/v')
|
|
16
|
-
uses: cadwallion/publish-rubygems-action@
|
|
18
|
+
uses: cadwallion/publish-rubygems-action@94a6f4cd5350581749c569b5001eecc864e3ad0b # v1.1.0
|
|
17
19
|
env:
|
|
18
20
|
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
|
|
19
21
|
RUBYGEMS_API_KEY: ${{secrets.RUBYGEMS_API_KEY}}
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 5.1.0 — 2026-09-06
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
* Search files through `client.files.search` with full-text, UUID, exact, metadata, tag, range, and image filters,
|
|
8
|
+
sorting, highlights, `include=appdata`, and POST-aware pagination that preserves the original search options.
|
|
9
|
+
* Manage per-file tags through `client.file_tags` with list, replace, and atomic add/delete operations.
|
|
10
|
+
* Apply tags while uploading files directly, in batches, from URLs, or through multipart uploads.
|
|
11
|
+
* Read tags from file resources returned by the REST API.
|
|
12
|
+
* Call the raw REST file-search and file-tag endpoints through `client.api.rest` when exact endpoint parity is needed.
|
|
13
|
+
|
|
14
|
+
## 5.0.1 — 2026-06-09
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
* Upload API requests now send the SDK-generated `User-Agent` instead of Faraday's default header.
|
|
19
|
+
* Multipart upload part requests to presigned URLs now include the same SDK-generated `User-Agent`.
|
|
20
|
+
|
|
3
21
|
## 5.0.0 — 2026-05-17
|
|
4
22
|
|
|
5
23
|
v5 is stable.
|
data/MIGRATING_V5.md
CHANGED
|
@@ -27,7 +27,7 @@ The examples expect `UPLOADCARE_PUBLIC_KEY` and `UPLOADCARE_SECRET_KEY` to come
|
|
|
27
27
|
## Recommended Migration Order
|
|
28
28
|
|
|
29
29
|
1. Introduce explicit `Uploadcare::Client` instances in your application.
|
|
30
|
-
2. Move app-facing code to `client.files`, `client.groups`, `client.uploads`, `client.project`, `client.webhooks`, `client.file_metadata`, `client.addons`, and `client.conversions`.
|
|
30
|
+
2. Move app-facing code to `client.files`, `client.groups`, `client.uploads`, `client.project`, `client.webhooks`, `client.file_metadata`, `client.file_tags`, `client.addons`, and `client.conversions`.
|
|
31
31
|
3. Keep `client.api.rest` and `client.api.upload` only where you need raw endpoint parity.
|
|
32
32
|
4. Audit return-type and error-handling assumptions.
|
|
33
33
|
5. Remove any app code that depends on internal transport classes.
|
|
@@ -201,6 +201,7 @@ These top-level resource constants still exist:
|
|
|
201
201
|
- `Uploadcare::Project`
|
|
202
202
|
- `Uploadcare::Webhook`
|
|
203
203
|
- `Uploadcare::FileMetadata`
|
|
204
|
+
- `Uploadcare::FileTags`
|
|
204
205
|
- `Uploadcare::DocumentConversion`
|
|
205
206
|
- `Uploadcare::VideoConversion`
|
|
206
207
|
|
data/README.md
CHANGED
|
@@ -27,6 +27,7 @@ The gem is built around:
|
|
|
27
27
|
- [Multi-Account Usage](#multi-account-usage)
|
|
28
28
|
- [Uploads](#uploads)
|
|
29
29
|
- [Files](#files)
|
|
30
|
+
- [File Tags](#file-tags)
|
|
30
31
|
- [Groups](#groups)
|
|
31
32
|
- [Project](#project)
|
|
32
33
|
- [Metadata](#metadata)
|
|
@@ -87,6 +88,7 @@ This is the default API you should use in applications:
|
|
|
87
88
|
- `client.project`
|
|
88
89
|
- `client.webhooks`
|
|
89
90
|
- `client.file_metadata`
|
|
91
|
+
- `client.file_tags`
|
|
90
92
|
- `client.addons`
|
|
91
93
|
- `client.conversions`
|
|
92
94
|
|
|
@@ -262,7 +264,12 @@ remote_file = client.uploads.upload("https://example.com/image.jpg", store: true
|
|
|
262
264
|
|
|
263
265
|
```ruby
|
|
264
266
|
file = File.open("photo.jpg", "rb") do |io|
|
|
265
|
-
client.files.upload(
|
|
267
|
+
client.files.upload(
|
|
268
|
+
io,
|
|
269
|
+
store: true,
|
|
270
|
+
metadata: { subsystem: "avatars" },
|
|
271
|
+
tags: ["avatar", "profile"]
|
|
272
|
+
)
|
|
266
273
|
end
|
|
267
274
|
```
|
|
268
275
|
|
|
@@ -274,7 +281,7 @@ files = [
|
|
|
274
281
|
File.open("photo-2.jpg", "rb")
|
|
275
282
|
]
|
|
276
283
|
|
|
277
|
-
uploaded = client.uploads.upload(files, store: true)
|
|
284
|
+
uploaded = client.uploads.upload(files, store: true, tags: ["gallery", "batch"])
|
|
278
285
|
|
|
279
286
|
files.each(&:close)
|
|
280
287
|
```
|
|
@@ -284,7 +291,11 @@ files.each(&:close)
|
|
|
284
291
|
Synchronous:
|
|
285
292
|
|
|
286
293
|
```ruby
|
|
287
|
-
file = client.files.upload_from_url(
|
|
294
|
+
file = client.files.upload_from_url(
|
|
295
|
+
"https://example.com/image.jpg",
|
|
296
|
+
store: true,
|
|
297
|
+
tags: ["remote", "example"]
|
|
298
|
+
)
|
|
288
299
|
```
|
|
289
300
|
|
|
290
301
|
Async:
|
|
@@ -306,7 +317,12 @@ Polling options for synchronous URL uploads:
|
|
|
306
317
|
|
|
307
318
|
```ruby
|
|
308
319
|
File.open("large-video.mp4", "rb") do |io|
|
|
309
|
-
file = client.uploads.multipart_upload(
|
|
320
|
+
file = client.uploads.multipart_upload(
|
|
321
|
+
file: io,
|
|
322
|
+
store: true,
|
|
323
|
+
threads: 4,
|
|
324
|
+
tags: ["video", "multipart"]
|
|
325
|
+
) do |progress|
|
|
310
326
|
uploaded = progress[:uploaded]
|
|
311
327
|
total = progress[:total]
|
|
312
328
|
puts "#{uploaded}/#{total}"
|
|
@@ -342,6 +358,7 @@ Common upload options:
|
|
|
342
358
|
|
|
343
359
|
- `store: true | false | "auto"`
|
|
344
360
|
- `metadata: { key: value }`
|
|
361
|
+
- `tags: ["tag-1", "tag_2"]`
|
|
345
362
|
- `signature: "..."`
|
|
346
363
|
- `expire: unix_timestamp`
|
|
347
364
|
- `async: true` for URL uploads
|
|
@@ -389,6 +406,50 @@ Filters and API parameters can still be passed through:
|
|
|
389
406
|
files = client.files.list(stored: true, removed: false, limit: 100)
|
|
390
407
|
```
|
|
391
408
|
|
|
409
|
+
### Search files
|
|
410
|
+
|
|
411
|
+
Search across filenames, file UUIDs, metadata, and detected MIME types:
|
|
412
|
+
|
|
413
|
+
```ruby
|
|
414
|
+
matches = client.files.search(
|
|
415
|
+
query: "invoice",
|
|
416
|
+
is_image: false,
|
|
417
|
+
sort: ["-datetime_uploaded"],
|
|
418
|
+
limit: 20
|
|
419
|
+
)
|
|
420
|
+
|
|
421
|
+
puts "Found #{matches.total} files"
|
|
422
|
+
matches.each do |file|
|
|
423
|
+
puts file.original_filename
|
|
424
|
+
puts file.highlight
|
|
425
|
+
end
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
Search accepts full-text `query` and field-specific `phrase` criteria, exact matches, ranges, and tag filters. All
|
|
429
|
+
top-level criteria are combined with AND:
|
|
430
|
+
|
|
431
|
+
```ruby
|
|
432
|
+
matches = client.files.search(
|
|
433
|
+
phrase: { original_filename: "report" },
|
|
434
|
+
exact: {
|
|
435
|
+
detected_mime_type: ["application/pdf"],
|
|
436
|
+
"metadata[department]" => ["finance"]
|
|
437
|
+
},
|
|
438
|
+
datetime_uploaded: { gte: "2026-01-01T00:00:00Z" },
|
|
439
|
+
size: { lte: 10 * 1024 * 1024 },
|
|
440
|
+
tags: { all: ["approved"], none: ["archived"] },
|
|
441
|
+
include: "appdata"
|
|
442
|
+
)
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
`limit`, `offset`, and `include` are sent as URL query parameters; search criteria are sent in the JSON body. Search
|
|
446
|
+
responses are `Uploadcare::Collections::FileSearchResult` objects and support `next_page`, `previous_page`, and `all`
|
|
447
|
+
like ordinary file lists. Subsequent pages automatically resend the original search criteria. Full-text values must be
|
|
448
|
+
at least four characters; use `exact` for shorter values. A field cannot appear in both `phrase` and `exact`, and every
|
|
449
|
+
request needs at least one search condition. `limit` accepts 1–100 results, while `offset + limit` cannot exceed 1,000.
|
|
450
|
+
For filter-only searches, pass an explicit `sort` for deterministic ordering. `fuzziness: true` enables typo-tolerant
|
|
451
|
+
matching but increases latency. Newly uploaded files may take a short time to appear in the search index.
|
|
452
|
+
|
|
392
453
|
### Resource operations
|
|
393
454
|
|
|
394
455
|
```ruby
|
|
@@ -424,6 +485,43 @@ copied = file.copy_to_local(options: { store: true })
|
|
|
424
485
|
remote_url = file.copy_to_remote(target: "custom_storage")
|
|
425
486
|
```
|
|
426
487
|
|
|
488
|
+
File responses expose the ordered tag list through `file.tags` when the field is present.
|
|
489
|
+
|
|
490
|
+
## File Tags
|
|
491
|
+
|
|
492
|
+
Tags can be attached during direct, URL, batch, and multipart uploads with the `tags:` option. The SDK normalizes tags to lowercase, strips surrounding whitespace, discards blank tags, removes duplicates while preserving order, and validates the platform limits.
|
|
493
|
+
Upload responses do not include tags; use `client.file_tags.list` or reload the file through the REST API to read them.
|
|
494
|
+
|
|
495
|
+
Read or replace the complete tag list:
|
|
496
|
+
|
|
497
|
+
```ruby
|
|
498
|
+
tags = client.file_tags.list(uuid: file.uuid)
|
|
499
|
+
|
|
500
|
+
change = client.file_tags.replace(
|
|
501
|
+
uuid: file.uuid,
|
|
502
|
+
tags: ["approved", "Summer"]
|
|
503
|
+
)
|
|
504
|
+
|
|
505
|
+
puts change.tags
|
|
506
|
+
puts change.added
|
|
507
|
+
puts change.deleted
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
Mutation responses expose the resulting ordered `tags`, the tags actually `added`, and the tags actually `deleted`.
|
|
511
|
+
Replacing with the same normalized set is safe and reports no additions or deletions.
|
|
512
|
+
|
|
513
|
+
Add and delete tags atomically (deletions are applied first):
|
|
514
|
+
|
|
515
|
+
```ruby
|
|
516
|
+
change = client.file_tags.update(
|
|
517
|
+
uuid: file.uuid,
|
|
518
|
+
add: ["featured"],
|
|
519
|
+
delete: ["draft"]
|
|
520
|
+
)
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
Passing an empty array to `replace` clears all tags. Tags may contain Latin letters, digits, hyphens, underscores, and dots; each tag is limited to 100 characters and each file to 50 tags.
|
|
524
|
+
|
|
427
525
|
## Groups
|
|
428
526
|
|
|
429
527
|
Create a group:
|
|
@@ -608,10 +706,14 @@ Upload API:
|
|
|
608
706
|
|
|
609
707
|
```ruby
|
|
610
708
|
File.open("photo.jpg", "rb") do |io|
|
|
611
|
-
client.api.upload.files.direct(file: io, store: true)
|
|
709
|
+
client.api.upload.files.direct(file: io, store: true, tags: ["photo", "example"])
|
|
612
710
|
end
|
|
613
711
|
|
|
614
|
-
client.api.upload.files.from_url(
|
|
712
|
+
client.api.upload.files.from_url(
|
|
713
|
+
source_url: "https://example.com/image.jpg",
|
|
714
|
+
async: true,
|
|
715
|
+
tags: ["remote", "example"]
|
|
716
|
+
)
|
|
615
717
|
client.api.upload.groups.create(files: ["uuid-1", "uuid-2"])
|
|
616
718
|
```
|
|
617
719
|
|
data/api_examples/README.md
CHANGED
|
@@ -21,14 +21,15 @@ Optional environment variables:
|
|
|
21
21
|
|
|
22
22
|
Verification:
|
|
23
23
|
|
|
24
|
-
-
|
|
25
|
-
-
|
|
24
|
+
- The endpoint suite was verified against a real Uploadcare demo account on `2026-08-07`.
|
|
25
|
+
- Run changed examples against a disposable project before release; they create and remove temporary resources.
|
|
26
26
|
|
|
27
27
|
## REST API 0.7
|
|
28
28
|
|
|
29
29
|
| Endpoint | Example file | Notes |
|
|
30
30
|
| --- | --- | --- |
|
|
31
31
|
| `GET /files/` | `api_examples/rest_api/get_files.rb` | Uses `client.files.list` |
|
|
32
|
+
| `POST /files/search/` | `api_examples/rest_api/post_files_search.rb` | Uses `client.files.search` with a UUID lookup |
|
|
32
33
|
| `PUT /files/{uuid}/storage/` | `api_examples/rest_api/put_files_uuid_storage.rb` | Uses `file.store` |
|
|
33
34
|
| `DELETE /files/{uuid}/storage/` | `api_examples/rest_api/delete_files_uuid_storage.rb` | Uses `file.delete` |
|
|
34
35
|
| `GET /files/{uuid}/` | `api_examples/rest_api/get_files_uuid.rb` | Uses `client.files.find` |
|
|
@@ -40,6 +41,9 @@ Verification:
|
|
|
40
41
|
| `GET /files/{uuid}/metadata/{key}/` | `api_examples/rest_api/get_files_uuid_metadata_key.rb` | Uses `client.file_metadata.show` |
|
|
41
42
|
| `PUT /files/{uuid}/metadata/{key}/` | `api_examples/rest_api/put_files_uuid_metadata_key.rb` | Uses `client.file_metadata.update` |
|
|
42
43
|
| `DELETE /files/{uuid}/metadata/{key}/` | `api_examples/rest_api/delete_files_uuid_metadata_key.rb` | Uses `client.file_metadata.delete` |
|
|
44
|
+
| `GET /files/{uuid}/tags/` | `api_examples/rest_api/get_files_uuid_tags.rb` | Uses `client.file_tags.list` |
|
|
45
|
+
| `PUT /files/{uuid}/tags/` | `api_examples/rest_api/put_files_uuid_tags.rb` | Uses `client.file_tags.replace` |
|
|
46
|
+
| `PATCH /files/{uuid}/tags/` | `api_examples/rest_api/patch_files_uuid_tags.rb` | Uses `client.file_tags.update` |
|
|
43
47
|
| `GET /groups/` | `api_examples/rest_api/get_groups.rb` | Uses `client.groups.list` |
|
|
44
48
|
| `GET /groups/{uuid}/` | `api_examples/rest_api/get_groups_uuid.rb` | Uses `client.groups.find` |
|
|
45
49
|
| `DELETE /groups/{uuid}/` | `api_examples/rest_api/delete_groups_uuid.rb` | Uses `group.delete` |
|
|
@@ -66,11 +70,11 @@ Verification:
|
|
|
66
70
|
|
|
67
71
|
| Endpoint | Example file | Notes |
|
|
68
72
|
| --- | --- | --- |
|
|
69
|
-
| `POST /base/` | `api_examples/upload_api/post_base.rb` | Uses raw upload API |
|
|
70
|
-
| `POST /multipart/start/` | `api_examples/upload_api/post_multipart_start.rb` | Starts and completes a real multipart upload |
|
|
73
|
+
| `POST /base/` | `api_examples/upload_api/post_base.rb` | Uses raw upload API with upload-time tags |
|
|
74
|
+
| `POST /multipart/start/` | `api_examples/upload_api/post_multipart_start.rb` | Starts and completes a real multipart upload with tags |
|
|
71
75
|
| `PUT <presigned-url-x>` | `api_examples/upload_api/put_multipart_part.rb` | Uploads one part via gem multipart helper |
|
|
72
76
|
| `POST /multipart/complete/` | `api_examples/upload_api/post_multipart_complete.rb` | Completes a real multipart upload |
|
|
73
|
-
| `POST /from_url/` | `api_examples/upload_api/post_from_url.rb` | Uses raw upload API |
|
|
77
|
+
| `POST /from_url/` | `api_examples/upload_api/post_from_url.rb` | Uses raw upload API with upload-time tags |
|
|
74
78
|
| `GET /from_url/status/` | `api_examples/upload_api/get_from_url_status.rb` | Starts async upload then checks status |
|
|
75
79
|
| `GET /info/` | `api_examples/upload_api/get_info.rb` | Uses raw upload API |
|
|
76
80
|
| `POST /group/` | `api_examples/upload_api/post_group.rb` | Uses raw upload API |
|
|
@@ -66,6 +66,21 @@ module ApiExamples::ExampleHelper
|
|
|
66
66
|
Array(files).each { |file| safe_delete_file(file) }
|
|
67
67
|
end
|
|
68
68
|
|
|
69
|
+
def wait_for_file_search(uuid:, timeout: 10, poll_interval: 0.5)
|
|
70
|
+
deadline = Process.clock_gettime(Process::CLOCK_MONOTONIC) + timeout
|
|
71
|
+
|
|
72
|
+
loop do
|
|
73
|
+
matches = client.files.search(query: uuid, limit: 20)
|
|
74
|
+
return matches if matches.any? { |match| match.uuid == uuid }
|
|
75
|
+
|
|
76
|
+
raise "Timed out waiting for file #{uuid} to appear in search" if Process.clock_gettime(
|
|
77
|
+
Process::CLOCK_MONOTONIC
|
|
78
|
+
) >= deadline
|
|
79
|
+
|
|
80
|
+
sleep poll_interval
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
69
84
|
def with_fixture_file(name)
|
|
70
85
|
handle = File.open(fixture_path(name), 'rb')
|
|
71
86
|
response = yield handle
|
|
@@ -124,7 +139,8 @@ module ApiExamples::ExampleHelper
|
|
|
124
139
|
size: file.size,
|
|
125
140
|
content_type: 'image/jpeg',
|
|
126
141
|
part_size: multipart_part_size,
|
|
127
|
-
store: true
|
|
142
|
+
store: true,
|
|
143
|
+
tags: %w[example multipart]
|
|
128
144
|
)
|
|
129
145
|
)
|
|
130
146
|
yield file, response
|
|
@@ -12,6 +12,20 @@ module ApiExamples::RunRestExample
|
|
|
12
12
|
client.project.current
|
|
13
13
|
when 'get_files.rb'
|
|
14
14
|
client.files.list(limit: 2)
|
|
15
|
+
when 'post_files_search.rb'
|
|
16
|
+
ApiExamples::ExampleHelper.with_uploaded_file do |file|
|
|
17
|
+
matches = ApiExamples::ExampleHelper.wait_for_file_search(uuid: file.uuid)
|
|
18
|
+
{
|
|
19
|
+
'total' => matches.total,
|
|
20
|
+
'results' => matches.map do |match|
|
|
21
|
+
{
|
|
22
|
+
'uuid' => match.uuid,
|
|
23
|
+
'original_filename' => match.original_filename,
|
|
24
|
+
'highlight' => match.highlight
|
|
25
|
+
}
|
|
26
|
+
end
|
|
27
|
+
}
|
|
28
|
+
end
|
|
15
29
|
when 'get_files_uuid.rb'
|
|
16
30
|
ApiExamples::ExampleHelper.with_uploaded_file do |file|
|
|
17
31
|
client.files.find(uuid: file.uuid)
|
|
@@ -93,6 +107,22 @@ module ApiExamples::RunRestExample
|
|
|
93
107
|
client.file_metadata.delete(uuid: file.uuid, key: 'color')
|
|
94
108
|
{ 'uuid' => file.uuid, 'key' => 'color', 'deleted' => true }
|
|
95
109
|
end
|
|
110
|
+
when 'get_files_uuid_tags.rb'
|
|
111
|
+
ApiExamples::ExampleHelper.with_uploaded_file do |file|
|
|
112
|
+
client.file_tags.replace(uuid: file.uuid, tags: %w[cat example])
|
|
113
|
+
client.file_tags.list(uuid: file.uuid)
|
|
114
|
+
end
|
|
115
|
+
when 'put_files_uuid_tags.rb'
|
|
116
|
+
ApiExamples::ExampleHelper.with_uploaded_file do |file|
|
|
117
|
+
change = client.file_tags.replace(uuid: file.uuid, tags: %w[approved example])
|
|
118
|
+
{ 'tags' => change.tags, 'added' => change.added, 'deleted' => change.deleted }
|
|
119
|
+
end
|
|
120
|
+
when 'patch_files_uuid_tags.rb'
|
|
121
|
+
ApiExamples::ExampleHelper.with_uploaded_file do |file|
|
|
122
|
+
client.file_tags.replace(uuid: file.uuid, tags: %w[draft example])
|
|
123
|
+
change = client.file_tags.update(uuid: file.uuid, add: ['featured'], delete: ['draft'])
|
|
124
|
+
{ 'tags' => change.tags, 'added' => change.added, 'deleted' => change.deleted }
|
|
125
|
+
end
|
|
96
126
|
when 'post_addons_aws_rekognition_detect_labels_execute.rb'
|
|
97
127
|
ApiExamples::ExampleHelper.with_uploaded_file do |file|
|
|
98
128
|
client.addons.aws_rekognition_detect_labels(uuid: file.uuid)
|
|
@@ -71,13 +71,19 @@ module ApiExamples::RunUploadExample
|
|
|
71
71
|
|
|
72
72
|
def run_base_upload(client)
|
|
73
73
|
ApiExamples::ExampleHelper.with_fixture_file('kitten.jpeg') do |handle|
|
|
74
|
-
ApiExamples::ExampleHelper.unwrap(
|
|
74
|
+
ApiExamples::ExampleHelper.unwrap(
|
|
75
|
+
client.api.upload.files.direct(file: handle, store: true, tags: %w[example base])
|
|
76
|
+
)
|
|
75
77
|
end
|
|
76
78
|
end
|
|
77
79
|
|
|
78
80
|
def run_url_upload(client)
|
|
79
81
|
response = ApiExamples::ExampleHelper.unwrap(
|
|
80
|
-
client.api.upload.files.from_url(
|
|
82
|
+
client.api.upload.files.from_url(
|
|
83
|
+
source_url: ApiExamples::ExampleHelper::SAMPLE_IMAGE_URL,
|
|
84
|
+
store: true,
|
|
85
|
+
tags: %w[example remote]
|
|
86
|
+
)
|
|
81
87
|
)
|
|
82
88
|
response
|
|
83
89
|
ensure
|
data/context7.json
CHANGED
|
@@ -52,9 +52,11 @@
|
|
|
52
52
|
"Prefer explicit `Uploadcare::Client` instances for application code, especially when an app uses more than one Uploadcare project.",
|
|
53
53
|
"Configure credentials with `Uploadcare::Client.new(public_key: ..., secret_key: ...)`, `Uploadcare.configure`, or the UPLOADCARE_PUBLIC_KEY and UPLOADCARE_SECRET_KEY environment variables.",
|
|
54
54
|
"Never hardcode real Uploadcare API keys in examples or application code; use environment variables or an application secrets store.",
|
|
55
|
-
"Use `client.files`, `client.groups`, `client.uploads`, `client.project`, `client.webhooks`, `client.file_metadata`, `client.addons`, and `client.conversions` for normal application workflows.",
|
|
55
|
+
"Use `client.files`, `client.groups`, `client.uploads`, `client.project`, `client.webhooks`, `client.file_metadata`, `client.file_tags`, `client.addons`, and `client.conversions` for normal application workflows.",
|
|
56
56
|
"Use `client.api.rest` and `client.api.upload` when exact REST API or Upload API endpoint parity is needed.",
|
|
57
57
|
"Use `client.uploads.upload` for automatic upload method selection and `client.uploads.multipart_upload` for explicit multipart uploads.",
|
|
58
|
+
"Use `client.files.search` for full-text and structured file search; search pagination resends the original POST criteria automatically.",
|
|
59
|
+
"Pass `tags: [...]` when uploading, and use `client.file_tags` to list, replace, add, or delete tags on an existing file.",
|
|
58
60
|
"Use `MIGRATING_V5.md` when upgrading applications from uploadcare-ruby v4.x to v5."
|
|
59
61
|
],
|
|
60
62
|
"previousVersions": [
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# uploadcare-ruby 5.1.0
|
|
2
|
+
|
|
3
|
+
This release adds file search and file-tag support to the v5 client API. It is backward compatible with 5.0.x and
|
|
4
|
+
does not require an application migration.
|
|
5
|
+
|
|
6
|
+
## Highlights
|
|
7
|
+
|
|
8
|
+
- Search files through `client.files.search` using text, UUID, metadata, tag, range, and image filters.
|
|
9
|
+
- Sort search results, inspect match highlights, request `appdata`, and paginate without rebuilding POST criteria.
|
|
10
|
+
- List, replace, add, and delete per-file tags through `client.file_tags`.
|
|
11
|
+
- Assign tags during direct, batch, URL, and multipart uploads.
|
|
12
|
+
- Read tags directly from returned file resources.
|
|
13
|
+
- Use `client.api.rest.files.search` and `client.api.rest.file_tags` for exact REST endpoint access.
|
|
14
|
+
|
|
15
|
+
## Upgrade Notes
|
|
16
|
+
|
|
17
|
+
- Requirement: Ruby `>= 3.3`.
|
|
18
|
+
- Update with `bundle update uploadcare-ruby` and run the application's test suite.
|
|
19
|
+
- No configuration or data migration is required from 5.0.x.
|
|
20
|
+
- File search results may be eventually consistent immediately after an upload; retry when a workflow searches for a
|
|
21
|
+
file it just created.
|
|
22
|
+
- Existing upload calls remain valid. Pass `tags: [...]` only where upload-time tagging is needed.
|
|
23
|
+
- Rollback: pin `uploadcare-ruby` to `5.0.1`.
|
|
24
|
+
|
|
25
|
+
## Examples
|
|
26
|
+
|
|
27
|
+
- File search: [`examples/file_search.rb`](../examples/file_search.rb)
|
|
28
|
+
- File tags: [`examples/file_tags.rb`](../examples/file_tags.rb)
|
|
29
|
+
- Raw REST examples: [`api_examples/README.md`](../api_examples/README.md)
|
|
30
|
+
|
|
31
|
+
## Full Changelog
|
|
32
|
+
|
|
33
|
+
See [CHANGELOG.md](../CHANGELOG.md).
|
data/examples/README.md
CHANGED
|
@@ -29,6 +29,10 @@ mise exec -- ruby examples/simple_upload.rb spec/fixtures/kitten.jpeg
|
|
|
29
29
|
Force multipart upload and show throughput details.
|
|
30
30
|
- `examples/url_upload.rb`
|
|
31
31
|
Upload a remote URL and show async polling as a follow-up example.
|
|
32
|
+
- `examples/file_search.rb`
|
|
33
|
+
Search files by text, inspect highlights, and follow the next page.
|
|
34
|
+
- `examples/file_tags.rb`
|
|
35
|
+
Upload a file with tags, then list, replace, add, and delete tags.
|
|
32
36
|
- `examples/group_creation.rb`
|
|
33
37
|
Upload multiple files, create a group, and print group details.
|
|
34
38
|
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require_relative '../lib/uploadcare'
|
|
5
|
+
require 'dotenv/load'
|
|
6
|
+
|
|
7
|
+
query = ARGV.join(' ')
|
|
8
|
+
|
|
9
|
+
if query.length < 4
|
|
10
|
+
puts 'Usage: ruby file_search.rb <query of at least four characters>'
|
|
11
|
+
puts 'Example: ruby file_search.rb invoice'
|
|
12
|
+
exit 1
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
begin
|
|
16
|
+
client = Uploadcare::Client.new(
|
|
17
|
+
public_key: ENV.fetch('UPLOADCARE_PUBLIC_KEY'),
|
|
18
|
+
secret_key: ENV.fetch('UPLOADCARE_SECRET_KEY')
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
results = client.files.search(query: query, sort: ['-datetime_uploaded'], limit: 20)
|
|
22
|
+
|
|
23
|
+
puts "Found #{results.total} files"
|
|
24
|
+
results.each do |file|
|
|
25
|
+
puts [file.uuid, file.original_filename, file.highlight].compact.join(' | ')
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
next_page = results.next_page
|
|
29
|
+
puts "Next page contains #{next_page.count} files" if next_page
|
|
30
|
+
rescue StandardError => e
|
|
31
|
+
warn "File search example failed: #{e.message}"
|
|
32
|
+
exit 1
|
|
33
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require_relative '../lib/uploadcare'
|
|
5
|
+
require 'dotenv/load'
|
|
6
|
+
|
|
7
|
+
file_path = ARGV[0]
|
|
8
|
+
|
|
9
|
+
unless file_path && File.exist?(file_path)
|
|
10
|
+
puts 'Usage: ruby file_tags.rb <file_path>'
|
|
11
|
+
puts 'Example: ruby file_tags.rb photo.jpg'
|
|
12
|
+
exit 1
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
begin
|
|
16
|
+
client = Uploadcare::Client.new(
|
|
17
|
+
public_key: ENV.fetch('UPLOADCARE_PUBLIC_KEY'),
|
|
18
|
+
secret_key: ENV.fetch('UPLOADCARE_SECRET_KEY')
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
file = File.open(file_path, 'rb') do |io|
|
|
22
|
+
client.files.upload(io, store: true, tags: %w[example draft])
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
puts "Uploaded #{file.uuid} with tags: #{client.file_tags.list(uuid: file.uuid).join(', ')}"
|
|
26
|
+
|
|
27
|
+
change = client.file_tags.replace(uuid: file.uuid, tags: %w[example approved])
|
|
28
|
+
puts "Replaced tags: #{change.tags.join(', ')}"
|
|
29
|
+
|
|
30
|
+
change = client.file_tags.update(uuid: file.uuid, add: ['featured'], delete: ['example'])
|
|
31
|
+
puts "Updated tags: #{change.tags.join(', ')}"
|
|
32
|
+
rescue StandardError => e
|
|
33
|
+
warn "File tag example failed: #{e.message}"
|
|
34
|
+
exit 1
|
|
35
|
+
ensure
|
|
36
|
+
client.api.rest.files.delete(uuid: file.uuid) if file&.uuid
|
|
37
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'uri'
|
|
4
|
+
|
|
5
|
+
# REST API endpoint for per-file tag operations.
|
|
6
|
+
#
|
|
7
|
+
# @see https://uploadcare.com/api-refs/rest-api/v0.7.0/#tag/File-tags
|
|
8
|
+
class Uploadcare::Api::Rest::FileTags
|
|
9
|
+
# @return [Uploadcare::Api::Rest] Parent REST client
|
|
10
|
+
attr_reader :rest
|
|
11
|
+
|
|
12
|
+
# @param rest [Uploadcare::Api::Rest] Parent REST client
|
|
13
|
+
def initialize(rest:)
|
|
14
|
+
@rest = rest
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Get the ordered list of tags for a file.
|
|
18
|
+
#
|
|
19
|
+
# @param uuid [String] File UUID
|
|
20
|
+
# @param request_options [Hash] Request options
|
|
21
|
+
# @return [Uploadcare::Result] Response containing the `tags` array
|
|
22
|
+
def list(uuid:, request_options: {})
|
|
23
|
+
rest.get(path: tags_path(uuid), params: {}, headers: {}, request_options: request_options)
|
|
24
|
+
end
|
|
25
|
+
alias index list
|
|
26
|
+
|
|
27
|
+
# Replace all tags for a file.
|
|
28
|
+
#
|
|
29
|
+
# @param uuid [String] File UUID
|
|
30
|
+
# @param tags [Array<String>] Complete replacement tag list
|
|
31
|
+
# @param request_options [Hash] Request options
|
|
32
|
+
# @return [Uploadcare::Result] Response containing tags, added, and deleted
|
|
33
|
+
def replace(uuid:, tags:, request_options: {})
|
|
34
|
+
rest.put(
|
|
35
|
+
path: tags_path(uuid), params: { tags: tags }, headers: {}, request_options: request_options
|
|
36
|
+
)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Atomically add and delete tags for a file.
|
|
40
|
+
#
|
|
41
|
+
# Deletions are applied before additions by the API.
|
|
42
|
+
#
|
|
43
|
+
# @param uuid [String] File UUID
|
|
44
|
+
# @param add [Array<String>, nil] Tags to add
|
|
45
|
+
# @param delete [Array<String>, nil] Tags to delete
|
|
46
|
+
# @param request_options [Hash] Request options
|
|
47
|
+
# @return [Uploadcare::Result] Response containing tags, added, and deleted
|
|
48
|
+
def update(uuid:, add: nil, delete: nil, request_options: {})
|
|
49
|
+
params = {}
|
|
50
|
+
params[:add] = add unless add.nil? || add.empty?
|
|
51
|
+
params[:delete] = delete unless delete.nil? || delete.empty?
|
|
52
|
+
body = params.empty? ? {}.to_json : params
|
|
53
|
+
rest.patch(path: tags_path(uuid), params: body, headers: {}, request_options: request_options)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
private
|
|
57
|
+
|
|
58
|
+
def tags_path(uuid)
|
|
59
|
+
encoded_uuid = URI.encode_www_form_component(uuid.to_s)
|
|
60
|
+
"/files/#{encoded_uuid}/tags/"
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
# REST API endpoint for file operations.
|
|
4
4
|
#
|
|
5
|
-
# Provides methods for listing, retrieving, storing, deleting, and copying files.
|
|
5
|
+
# Provides methods for listing, searching, retrieving, storing, deleting, and copying files.
|
|
6
6
|
#
|
|
7
7
|
# @example
|
|
8
8
|
# rest = Uploadcare::Api::Rest.new(config: config)
|
|
9
9
|
# rest.files.list(params: { limit: 10 })
|
|
10
|
+
# rest.files.search(params: { query: "invoice" }, query: { limit: 20 })
|
|
10
11
|
# rest.files.info(uuid: "file-uuid")
|
|
11
12
|
#
|
|
12
13
|
# @see https://uploadcare.com/api-refs/rest-api/v0.7.0/#tag/File
|
|
@@ -29,6 +30,22 @@ class Uploadcare::Api::Rest::Files
|
|
|
29
30
|
rest.get(path: '/files/', params: params, headers: {}, request_options: request_options)
|
|
30
31
|
end
|
|
31
32
|
|
|
33
|
+
# Search files by full-text criteria and structured filters.
|
|
34
|
+
#
|
|
35
|
+
# Search criteria belong in the JSON request body (`params`). Pagination and
|
|
36
|
+
# response expansion options belong in the query string (`query`).
|
|
37
|
+
#
|
|
38
|
+
# @param params [Hash] Search body (query, phrase, exact, ranges, tags, etc.)
|
|
39
|
+
# @param query [Hash] Query parameters (limit, offset, include)
|
|
40
|
+
# @param request_options [Hash] Request options
|
|
41
|
+
# @return [Uploadcare::Result] Paginated file search results
|
|
42
|
+
# @see https://uploadcare.com/api-refs/rest-api/v0.7.0/#tag/File/operation/searchFiles
|
|
43
|
+
def search(params: {}, query: {}, request_options: {})
|
|
44
|
+
rest.post(
|
|
45
|
+
path: '/files/search/', params: params, query: query, headers: {}, request_options: request_options
|
|
46
|
+
)
|
|
47
|
+
end
|
|
48
|
+
|
|
32
49
|
# Get file information by UUID.
|
|
33
50
|
#
|
|
34
51
|
# @param uuid [String] The file UUID
|