Add a list directory method to the S3 persistor

GitOrigin-RevId: 6ecff3eb457dc2168ca49ff9409bb09fa932781c
This commit is contained in:
Andrew Rumble
2025-03-19 10:57:53 +00:00
committed by Copybot
parent 48379a9d86
commit 4157f8ca00
3 changed files with 70 additions and 22 deletions

View File

@@ -33,6 +33,10 @@ const AES256_KEY_LENGTH = 32
* @property {() => Promise<Array<RootKeyEncryptionKey>>} getRootKeyEncryptionKeys
*/
/**
* @typedef {import('./types').ListDirectoryResult} ListDirectoryResult
*/
/**
* Helper function to make TS happy when accessing error properties
* AWSError is not an actual class, so we cannot use instanceof.
@@ -391,9 +395,9 @@ class PerProjectEncryptedS3Persistor extends S3Persistor {
* A general "cache" for project keys is another alternative. For now, use a helper class.
*/
class CachedPerProjectEncryptedS3Persistor {
/** @type SSECOptions */
/** @type SSECOptions */
#projectKeyOptions
/** @type PerProjectEncryptedS3Persistor */
/** @type PerProjectEncryptedS3Persistor */
#parent
/**
@@ -424,6 +428,16 @@ class CachedPerProjectEncryptedS3Persistor {
return await this.#parent.getObjectSize(bucketName, path)
}
/**
*
* @param {string} bucketName
* @param {string} path
* @return {Promise<ListDirectoryResult>}
*/
async listDirectory(bucketName, path) {
return await this.#parent.listDirectory(bucketName, path)
}
/**
* @param {string} bucketName
* @param {string} path

View File

@@ -20,6 +20,18 @@ const { URL } = require('node:url')
const { WriteError, ReadError, NotFoundError } = require('./Errors')
const zlib = require('node:zlib')
/**
* @typedef {import('aws-sdk/clients/s3').ListObjectsV2Output} ListObjectsV2Output
*/
/**
* @typedef {import('aws-sdk/clients/s3').Object} S3Object
*/
/**
* @typedef {import('./types').ListDirectoryResult} ListDirectoryResult
*/
/**
* Wrapper with private fields to avoid revealing them on console, JSON.stringify or similar.
*/
@@ -266,26 +278,12 @@ class S3Persistor extends AbstractPersistor {
* @return {Promise<void>}
*/
async deleteDirectory(bucketName, key, continuationToken) {
let response
const options = { Bucket: bucketName, Prefix: key }
if (continuationToken) {
options.ContinuationToken = continuationToken
}
try {
response = await this._getClientForBucket(bucketName)
.listObjectsV2(options)
.promise()
} catch (err) {
throw PersistorHelper.wrapError(
err,
'failed to list objects in S3',
{ bucketName, key },
ReadError
)
}
const objects = response.Contents?.map(item => ({ Key: item.Key || '' }))
const { contents, response } = await this.listDirectory(
bucketName,
key,
continuationToken
)
const objects = contents.map(item => ({ Key: item.Key || '' }))
if (objects?.length) {
try {
await this._getClientForBucket(bucketName)
@@ -316,6 +314,36 @@ class S3Persistor extends AbstractPersistor {
}
}
/**
*
* @param {string} bucketName
* @param {string} key
* @param {string} [continuationToken]
* @return {Promise<ListDirectoryResult>}
*/
async listDirectory(bucketName, key, continuationToken) {
let response
const options = { Bucket: bucketName, Prefix: key }
if (continuationToken) {
options.ContinuationToken = continuationToken
}
try {
response = await this._getClientForBucket(bucketName)
.listObjectsV2(options)
.promise()
} catch (err) {
throw PersistorHelper.wrapError(
err,
'failed to list objects in S3',
{ bucketName, key },
ReadError
)
}
return { contents: response.Contents ?? [], response }
}
/**
* @param {string} bucketName
* @param {string} key

View File

@@ -0,0 +1,6 @@
import type { ListObjectsV2Output, Object } from 'aws-sdk/clients/s3'
export type ListDirectoryResult = {
contents: Array<Object>
response: ListObjectsV2Output
}