Repository navigation
data: downloading a tenant export whose archive is no longer stored answers 410 and expires it (#8590) - #8591
Merged
Conversation
…nswers 410 and expires it (#8590) The export's row lives in the database and its archive in the CMS, and the two can part: the storage of a replaced instance, a restore from an older backup, a file removed by hand. The row was then still listed as ready and its download answered 500, because TenantExportArchives.open looked the archive up with getObjectByPath, which every CMS (internal, S3, SharePoint) answers for a missing path with the same plain IOException as for a storage it cannot read. TenantExportArchives.open now walks the folders, as the class already does for its other lookups, and answers empty when there is no archive at the path, while a CMS that cannot be read still throws. The new TenantExportService.openArchive turns an empty answer into 410 "The export's archive is no longer available" and sets the export's expiresAt to now, so the list stops offering it and the hourly housekeeping removes it. The endpoint opens the archive through it before it sets any status or header. Deleting such an export was already 204: deleteExportFolder treats finding nothing as success. Verified: TenantExport*Test (29, three new service cases and three new archive cases - present, missing folder/file, storage failure) and the data-processes unit suite; TenantExportIT (7, including the new case that deletes a completed export's archive from the CMS and asserts 410, an expiresAt not in the future, then DELETE 204); formatter:validate with the cache wiped; release-profile javadoc of data-processes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
Cause
A tenant export's row lives in the database and its archive in the CMS, and the two can part (the storage of a replaced instance, a restore from an older backup, a file removed by hand). The row was still listed as
COMPLETEDand its download answered 500:TenantExportArchives.openlooked the archive up withgetObjectByPath, which every CMS backend (internal, S3, SharePoint) answers for a missing path with the same plainIOExceptionit throws for a storage it cannot read. There was no way to tell the two apart at that call.Change
TenantExportArchives.opennow walks the folders withgetChildren(), as the class already does for its other lookups (deleteExportFolder,pack). It answersOptional.empty()when there is no archive at the path, and a CMS that cannot be read still throwsIOException.TenantExportService.openArchive(export): when the archive is missing, it sets the export'sexpiresAtto now (its own transaction) and answers 410 "The export's archive is no longer available". The list then stops offering the export as downloadable, and the hourly housekeeping removes it on its next pass. A real read failure stays 500.TenantExportEndpoint.archiveopens the archive through the service before it sets any status or header, so a 410 never follows a started download. Its unusedTenantExportArchivesdependency is gone.DELETE /{id}of such an export was already204, becausedeleteExportFoldertreats finding nothing as success. The new IT pins this.data-processestakeschemistry-opencmis-commons-apiin test scope, the same asengine-document:CmisFoldernames an OpenCMIS enum, so mocking it needs the class..claude/docs/tenants.md(Expiry bullet) records the rule.Verification
TenantExport*Test: 29 green. That includes three new service cases (archive present; archive missing → 410 +expiresAt= clock now, thenexpire()removes the row; read failure →IOExceptionand no expiry) and three new archive cases (present; missing folder, missing file, or a folder of the archive's name → empty; storage failure →IOException). The fulldata-processesunit suite is green.TenantExportIT: 7 green, including the newanExportWhoseArchiveIsNoLongerStoredIsGoneAndCanBeDeleted. It completes an export, deletes its archive from the tenant's CMS, then checks thatGET .../archiveis 410 with the reason, thatexpiresAtis not in the future, thatDELETEis 204 and that the export is then 404. The log shows the WARN and no stack trace.mvn -T 1C formatter:validatewith the formatter cache wiped: green.data-processes: green.Docs: dirigible-io/dirigible-io.github.io#289 (the missing archive under
410of "Download the archive").Fixes #8590
🤖 Generated with Claude Code