1
14
Fork
You've already forked s3mini
1

Support versioning #3

Open
opened 2026年06月22日 00:54:52 +02:00 by pavulon · 6 comments
Contributor
Copy link

It would be cool to be able to work with versioned objects.

Already working:

Not working:

  • list all objects versions
    almost works: s3.listObjects(undefined, undefined, undefined, { versions: true }) returns an empty response, but that's an easy fix (just add a different top level xml object name)

  • restoring old version by

    • copying
      no way to pass versionId query param
      theoretically it could be passed like s3.copyObject('file.jpg?versionId=123', 'file.jpg') (doesn't work now because it gets escaped), but it's probably better to go the opts object route

    • removing specific version

      • single
        no way to pass versionId query param

      • bulk
        no way to pass <VersionId> prop to xml

It would be cool to be able to work with [versioned objects](https://docs.aws.amazon.com/AmazonS3/latest/userguide/manage-objects-versioned-bucket.html). Already working: - [get version id on upload](https://docs.aws.amazon.com/AmazonS3/latest/userguide/AddingObjectstoVersioningEnabledBuckets.html) can be read from `response.headers['x-amz-version-id']` - [get object by version id](https://docs.aws.amazon.com/AmazonS3/latest/userguide/RetrievingObjectVersions.html) `s3.getObject('file.jpg', { versionId: '123' })` Not working: - [list all objects versions](https://docs.aws.amazon.com/AmazonS3/latest/userguide/list-obj-version-enabled-bucket.html) almost works: `s3.listObjects(undefined, undefined, undefined, { versions: true })` returns an empty response, but that's an easy fix (just add a different top level xml object name) - [restoring old version](https://docs.aws.amazon.com/AmazonS3/latest/userguide/RestoringPreviousVersions.html) by - [copying](https://docs.aws.amazon.com/AmazonS3/latest/API/API_CopyObject.html#AmazonS3-CopyObject-request-header-CopySource) no way to pass `versionId` query param theoretically it could be passed like `s3.copyObject('file.jpg?versionId=123', 'file.jpg')` (doesn't work now because it gets escaped), but it's probably better to go the `opts` object route - removing specific version - [single](https://docs.aws.amazon.com/AmazonS3/latest/userguide/DeletingObjectVersions.html) no way to pass `versionId` query param - [bulk](https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteObjects.html#API_DeleteObjects_RequestSyntax) no way to pass `<VersionId>` prop to xml

Great catch @pavulon ! Let's fix this into the next release .... if you have time, feel free to send PR otherwise I will fix around 2/3rd July.

Great catch @pavulon ! Let's fix this into the next release .... if you have time, feel free to send PR otherwise I will fix around 2/3rd July.
Author
Contributor
Copy link

I can take a look, but I'm not sure what's the preferred approach for some of these.

  • Listing

    When you send versions query param, you get

    <ListVersionsResult>
     <Version>...</Version>
     <Version>...</Version>
     <Version>...</Version>
    </ListVersionsResult>
    

    instead of

    <ListBucketResult>
     <Contents>...</Contents>
     <Contents>...</Contents>
     <Contents>...</Contents>
    </ListBucketResult>
    

    that's an easy fix.

    But <Version> has two additional keys: <VersionId> & <IsLatest>

    • should these be just added to ListObject? Should they be optional strings, or default to ''?
    • do we want a bit more type safety, and have a separate VersionsListObject (or something) that is returned only when you pass a param for versions? which would probably require a breaking change to the function signature

    BTW a little code suggestion/nitpick, but instead of trying PascalCase and camelCase for each key, couldn't the XML parser just normalize them?

  • Copying

    I guess add versionId to CopyObjectOptions?
    eg. await s3.copyObject('test.jpg', 'test.jpg', { versionId: '123' })

  • Deleting

    Have an alternative object syntax for removing objects?
    So you can either do s3.deleteObject('file.jpg') or s3.deleteObject({ key: 'file.jpg', versionId: '123' })

    But if we're allowing to pass this option, I guess others should also be allowed?

I can take a look, but I'm not sure what's the preferred approach for some of these. - Listing When you send `versions` query param, you get ```xml <ListVersionsResult> <Version>...</Version> <Version>...</Version> <Version>...</Version> </ListVersionsResult> ``` instead of ```xml <ListBucketResult> <Contents>...</Contents> <Contents>...</Contents> <Contents>...</Contents> </ListBucketResult> ``` that's an easy fix. But `<Version>` has two additional keys: `<VersionId>` & `<IsLatest>` - should these be just added to `ListObject`? Should they be optional strings, or default to `''`? - do we want a bit more type safety, and have a separate `VersionsListObject` (or something) that is returned only when you pass a param for versions? which would probably require a breaking change to the function signature BTW a little code suggestion/nitpick, but instead of [trying PascalCase and camelCase for each key](https://codeberg.org/thinking_tools/s3mini/src/commit/722d813cab832137a0924706f059dcb72df27b17/src/S3.ts#L680-L684), couldn't the XML parser just normalize them? - Copying I guess add `versionId` to `CopyObjectOptions`?\ eg. `await s3.copyObject('test.jpg', 'test.jpg', { versionId: '123' })` - Deleting Have an alternative object syntax for removing objects?\ So you can either do `s3.deleteObject('file.jpg')` or `s3.deleteObject({ key: 'file.jpg', versionId: '123' })` But if we're allowing to pass this option, I guess [others](https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteObjects.html#API_DeleteObjects_Example_12) should also be allowed?

I am very much open to your suggestions and recommendations, but here is what i would do:

  • listing: new public function listObjectVersions() which returns own type with versionId and isLatest as optional ... that way we will not break already used implementations and function itself may just "wrap" list with optional prop to avoid code duplicity. It's also more similar to aws API (i guess)

Ad nitpick: is it faster to sanitize each key than to do ?? check on the fly? I would vouch for the fastest option.

  • Copying: yes, lets extend that as an optional param

  • Deleting: I would introduce interface DeleteObject with versionId as a optional param ... so maybe like s3.deleteObject({ key: 'file.jpg', versionId: '123' }) which can be used in deleteObjects as well. And we add that type to deleteObject(key|DeleteObject) and deleteObjects(keys[]|DeleteObject[]) - what you think?

(sorry for typos)

I am very much open to your suggestions and recommendations, but here is what i would do: - listing: new public function listObjectVersions() which returns own type with `versionId` and `isLatest` as optional ... that way we will not break already used implementations and function itself may just "wrap" list with optional prop to avoid code duplicity. It's also more similar to aws API (i guess) Ad nitpick: is it faster to sanitize each key than to do `??` check on the fly? I would vouch for the fastest option. - Copying: yes, lets extend that as an optional param - Deleting: I would introduce `interface DeleteObject` with versionId as a optional param ... so maybe like s3.deleteObject({ key: 'file.jpg', versionId: '123' }) which can be used in `deleteObjects` as well. And we add that type to deleteObject(key|DeleteObject) and deleteObjects(keys[]|DeleteObject[]) - what you think? (sorry for typos)

@pavulon just checking if any updates .... wanna make an PR or shall I take over - I should be free in a ~ week or so.

@pavulon just checking if any updates .... wanna make an PR or shall I take over - I should be free in a ~ week or so.
Author
Contributor
Copy link
  • Actually I guess it could work without breaking anything: TS Playground

    Adding listObjectVersions() would be a bit easier, but then do we also add listObjectVersionsPaged()? There would be 4 list functions in total. It would be more similar to AWS, but we're not adding versioning-specific variants for other operations like copy/move/delete, so I thought it would be more consistent with this library's simpler API.

    Ad nitpick: is it faster to sanitize each key than to do ?? check on the fly? I would vouch for the fastest option

    I just had this simplistic idea to do

    - const tagName = match[1];
    + const tagName = capitalize(match[1]);
    

    in the XML parser, but then I realized there are special cases like ETag...

  • copying: 👍

  • deleting: 👍

- Actually I guess it could work without breaking anything: [TS Playground](https://www.typescriptlang.org/play/?#code/JYOwLgpgTgZghgYwgAgDLAM5gPICMBWECYyA3gFDLIDSEAngFzJZSgDmA3JcgMrABeEJiACuAW1zQuVVHCwBZAPYATYDGARlTACJxI05AFEAKnDZMW7AzzCKoZiAGEANnIwWwrEJ3IBfcuSgkLCIKOhYeITEAGrQGMCKIMgQAB6QIMoYaJg4BEQkFFSxUPGJAJJazJ5W3GUYspBYTLiKis4QcCBc-uRgdAAOYTnY-WAJIFkAvGTcVADaANb0Hl5sALpMIiALIIoA7l2zyABuceMYAPxMniIQ3QEwW8TjyM7DecQYADzGyWkQGSy4Rwo3OyAAPsgtsoIOoQJoAHwACm4MLeYmAwWQ0wA5AB6HEAGm4-SgsOAKWxyBxRO4YjgKVodEuwnEkigxKoilGLOQxmJAEomL9UulMmQTmdEu5kDcUL5kBdshEPmBiqUQHM1sgmMDIvktTMqHi8chgGJ+u0xACwHpxtwwAALKD7ZDwvZGKAuqBIgX3cgIaUkN5YKkh3JRMAYJE0wnUonIACMAAZk3HSMxFNaRmAmDjnG0cchfALyOG5sm1gA6dXjCoBQMTEinEpg6bh-WfGMJ2NJ1PpyWt6XXKC3OMYLMQHN5gvOIsl8gtjUYCvV2vlZTkIA) Adding `listObjectVersions()` would be a bit easier, but then do we also add `listObjectVersionsPaged()`? There would be 4 list functions in total. It would be more similar to AWS, but we're not adding versioning-specific variants for other operations like copy/move/delete, so I thought it would be more consistent with this library's simpler API. >Ad nitpick: is it faster to sanitize each key than to do ?? check on the fly? I would vouch for the fastest option I just had this simplistic idea to do ```diff - const tagName = match[1]; + const tagName = capitalize(match[1]); ``` in the XML parser, but then I realized there are special cases like `ETag`... - copying: 👍 - deleting: 👍

You are right @pavulon - good point... thank you! Let's use the extra param ...

You are right @pavulon - good point... thank you! Let's use the extra param ...
Sign in to join this conversation.
No Branch/Tag specified
dev
release
v0.9.4
v0.9.3
v0.9.2
v0.9.1
v0.9.0
v0.8.1
v0.8.0
v0.7.1
v0.7.0
v0.6.0
v0.5.0
v0.4.0
v0.3.0
v0.2.0
v0.1.1
v0.1.0
Milestone
Clear milestone
No items
No milestone
Projects
Clear projects
No items
No project
Assignees
Clear assignees
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
thinking_tools/s3mini#3
Reference in a new issue
thinking_tools/s3mini
No description provided.
Delete branch "%!s()"

Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?