Add support for file tags and file search - #319
Closed
loookashow wants to merge 4 commits into
Closed
Conversation
Expose the REST API v0.7 file tags endpoints in the SDK.
- TagsAPI (uploadcare.tags_api) with get/replace/update, covering GET, PUT
and PATCH on /files/{uuid}/tags/
- File: tags property plus get_tags(), set_tags() and update_tags()
- tags argument for Uploadcare.upload(), upload_files() and
multipart_upload(), sent as the comma-separated tags form field
- tags in FileInfo
- ucare get_file_tags, set_file_tags and update_file_tags commands, and a
--tags option for ucare upload
- TagValidationError for tags exceeding the API limits
Tags are normalized before validation, the way the API normalizes them, so
values it would accept after its own normalization are not rejected locally.
update() with no arguments sends an empty body, since the endpoint documents
both fields as optional; requiring at least one of --add/--delete is a CLI
concern instead. The 50-tags-per-file limit does not apply to PATCH delete,
which is a candidate list where absent tags are ignored.
Uploads from url do not support tags upstream, so passing them raises
InvalidParamError rather than dropping them silently.
FileInfo.model_dump(), and therefore File.info, now always contains a tags
key: None for responses that do not report tags, [] for files without any.
Expose the REST API v0.7 POST /files/search/ endpoint in the SDK. - Uploadcare.search_files(), returning one typed page, and Uploadcare.iterate_search_files(), which walks pages - FilesAPI.search() - Typed requests: FileSearchRequest with query, phrase, exact, datetime_uploaded, size, is_image and tags conditions plus the fuzziness and sort modifiers, built from SearchPhrase, SearchExact, DatetimeRange, SizeRange, TagsFilter and SearchSort. A plain dict in the same shape works too - FileSearchResponse with next, previous, total, per_page and results, each result a FileSearchInfo carrying a SearchHighlight - ucare search_files command Requests are validated locally against the documented API constraints before any request is made, so a malformed search fails fast with a clear message. Paging never requests the response's next URL: it is an absolute, server-supplied URL, and the REST client attaches credentials to whatever URL it is given. next is used only as a has-more signal and the offset is computed locally, clamped so that no request exceeds the 1000-result window. total is not used as a stop condition because the API documents it as approximate for large result sets. iterate_search_files() warns when asked to page through a filter-only request without sort, whose result order the API leaves undefined.
Verified against the live API: after a direct upload nothing is cached, so reading File.tags fetches the file info and returns the stored tags. The None case applies to multipart uploads, where the upload response is cached as the file info and carries no tags key. Pin both paths with tests.
Cover both features against the live REST API: tags CRUD, tags on upload, tags in file info and listings, search conditions, modifiers, highlight, appdata and pagination, plus the new CLI commands. Tests create and delete their own files, and upload a PNG rather than a text file so they work on projects that restrict uploadable types. Search indexing is asynchronous, taking on the order of ten seconds, so looking for a freshly uploaded file polls through wait_until_searchable while the rest search over content the project already holds. Document the delay. Correct the highlight documentation: the API reference says the field is absent for filter-only matches, but the live API sends an empty object.
|
❌ The last analysis has failed. |
Member
😿 |
This was referenced Aug 5, 2026
Contributor
Author
|
@dmitry-mukhin split into two reviewable PRs as requested:
Search's Closing this one in favour of those two. 🤖 Addressed by Claude Code |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Exposes two File Platform capabilities added in REST API v0.7 in the SDK: file tags and file search. Both require the
Accept: application/vnd.uploadcare-v0.7+jsonheader, which the SDK already sends (conf.api_versionis0.7), so no header work was needed.Bumps the version to 6.3.0 (two additive features, no breaking changes).
File tags
TagsAPI(uploadcare.tags_api) mirroringMetadataAPI:get()/replace()/update(), coveringGET/PUT/PATCHon/files/{uuid}/tags/.File: atagsproperty plusget_tags(),set_tags(),update_tags().tagsargument onUploadcare.upload(),upload_files()andmultipart_upload(), sent as the comma-separatedtagsform field.tagsonFileInfo.ucarecommandsget_file_tags,set_file_tags,update_file_tags, and--tagsonucare upload.TagValidationErrorfor tags exceeding the API limits (50 per file, 100 chars each, Latin letters/digits/-/_/.).Notable decisions:
InvalidParamErrorrather than silently dropping them.FileInfo.model_dump()— and thereforeFile.info— now always contains atagskey (Nonewhen the endpoint does not report tags,[]for a file without tags). Called out in HISTORY.File search
Uploadcare.search_files(), returning one typed page, andUploadcare.iterate_search_files(), which walks pages (limit= total to yield,request_limit= page size, as elsewhere in the SDK).FilesAPI.search()forPOST /files/search/.FileSearchRequest(query,phrase,exact,datetime_uploaded,size,is_image,tags+fuzziness/sort), also accepting a plain dict. All documented API constraints are validated locally before a request is made.FileSearchResponse(next,previous,total,per_page,results), each result aFileSearchInfowith aSearchHighlight.ucare search_filescommand.Notable decisions:
nextURL — the REST client attaches credentials to any URL it is given, sonextis used only as a has-more signal and the offset is computed locally, clamped to the 1000-result window.totalis not used as a stop condition, since the API documents it as approximate for large result sets.iterate_search_files()warns when asked to page through a filter-only request withoutsort, whose result order the API leaves undefined.Testing
unittest.mockfor request shape (VCR matches on method + URI only) and hand-authored cassettes for response parsing.tests/integration/) run the whole surface against the live REST API — tags CRUD, tags on upload, search conditions/modifiers/highlight/appdata/pagination, and every new CLI command. They create and delete their own files and upload a PNG rather than a text file so they work on projects that restrict uploadable types.File.tagsafter a direct upload fetches the info and returns the stored tags (only multipart leaves itNone), andhighlightfor a filter-only match arrives as an empty object rather than being absent as the reference states. Search indexing is asynchronous (~10s), which the integration tests poll for and the docs now note.make lintis clean; functional + Django suites pass; the integration suite passes against a live project.🤖 Generated with Claude Code