From 193f2ee3fe5d1aea227cbe68498187fc01811411 Mon Sep 17 00:00:00 2001 From: Eric Doughty-Papassideris Date: Mon, 24 Feb 2025 01:06:01 +0100 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20back:=20add=20enumeratePathsForFile?= =?UTF-8?q?=20to=20storage=20api=20(#799)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../services/storage/connectors/S3/s3-service.ts | 8 ++++++++ .../services/storage/connectors/local/service.ts | 6 ++++++ .../services/storage/default-storage-strategy.ts | 4 ++++ .../platform/services/storage/oneof-storage-strategy.ts | 9 +++++++++ .../node/src/core/platform/services/storage/provider.ts | 5 +++++ .../core/platform/services/storage/storage-service.ts | 4 ++++ 6 files changed, 36 insertions(+) diff --git a/tdrive/backend/node/src/core/platform/services/storage/connectors/S3/s3-service.ts b/tdrive/backend/node/src/core/platform/services/storage/connectors/S3/s3-service.ts index e5c1d1f3..dbe5cc0b 100644 --- a/tdrive/backend/node/src/core/platform/services/storage/connectors/S3/s3-service.ts +++ b/tdrive/backend/node/src/core/platform/services/storage/connectors/S3/s3-service.ts @@ -149,4 +149,12 @@ export default class S3ConnectorService implements StorageConnectorAPI { } return true; } + + async enumeratePathsForFile(filePath: string): Promise { + return ( + await ( + await this.client.listObjectsV2(this.minioConfiguration.bucket, filePath, true) + ).toArray() + ).map(({ name }) => name as string); + } } diff --git a/tdrive/backend/node/src/core/platform/services/storage/connectors/local/service.ts b/tdrive/backend/node/src/core/platform/services/storage/connectors/local/service.ts index 682411c3..8bbfe0bf 100644 --- a/tdrive/backend/node/src/core/platform/services/storage/connectors/local/service.ts +++ b/tdrive/backend/node/src/core/platform/services/storage/connectors/local/service.ts @@ -104,4 +104,10 @@ export default class LocalConnectorService implements StorageConnectorAPI { const fullPath = this.getFullPath(path); return fs.existsSync(fullPath); } + + async enumeratePathsForFile(filePath: string): Promise { + return (await fs.promises.readdir(this.getFullPath(filePath), { recursive: true })).map( + item => filePath + "/" + item, + ); + } } diff --git a/tdrive/backend/node/src/core/platform/services/storage/default-storage-strategy.ts b/tdrive/backend/node/src/core/platform/services/storage/default-storage-strategy.ts index 8e14112d..99dcc5bf 100644 --- a/tdrive/backend/node/src/core/platform/services/storage/default-storage-strategy.ts +++ b/tdrive/backend/node/src/core/platform/services/storage/default-storage-strategy.ts @@ -40,6 +40,10 @@ export class DefaultStorageStrategy implements StorageConnectorAPI { return this.connector.exists(path, options, context); }; + enumeratePathsForFile = (filePath: string): Promise => { + return this.connector.enumeratePathsForFile(filePath); + }; + remove = ( path: string, options?: DeleteOptions, diff --git a/tdrive/backend/node/src/core/platform/services/storage/oneof-storage-strategy.ts b/tdrive/backend/node/src/core/platform/services/storage/oneof-storage-strategy.ts index f6a6f3dc..865c024e 100644 --- a/tdrive/backend/node/src/core/platform/services/storage/oneof-storage-strategy.ts +++ b/tdrive/backend/node/src/core/platform/services/storage/oneof-storage-strategy.ts @@ -158,6 +158,15 @@ export class OneOfStorageStrategy implements StorageConnectorAPI { return false; }; + /** If any of the storages fails, then fail. Otherwise return a union of all paths related to that file's path */ + async enumeratePathsForFile(filePath: string): Promise { + const paths = new Set(); + // This needs all storages to respond. Until then, deletion must be retried, so fail. + for (const storage of this.storages) + for (const path of await storage.enumeratePathsForFile(filePath)) paths.add(path); + return [...paths.values()]; + } + /** * Removes a file from all configured storages. * @param path - The path of the file to be removed. diff --git a/tdrive/backend/node/src/core/platform/services/storage/provider.ts b/tdrive/backend/node/src/core/platform/services/storage/provider.ts index fb258876..f7131634 100644 --- a/tdrive/backend/node/src/core/platform/services/storage/provider.ts +++ b/tdrive/backend/node/src/core/platform/services/storage/provider.ts @@ -63,6 +63,11 @@ export interface StorageConnectorAPI extends IServiceDiagnosticProvider { * @param path */ remove(path: string, options?: DeleteOptions, context?: ExecutionContext): Promise; + + /** + * Enumerate all physical storage paths related to the provided file path + */ + enumeratePathsForFile(filePath: string): Promise; } export default interface StorageAPI extends TdriveServiceProvider, StorageConnectorAPI { diff --git a/tdrive/backend/node/src/core/platform/services/storage/storage-service.ts b/tdrive/backend/node/src/core/platform/services/storage/storage-service.ts index 710832f6..816c46d6 100644 --- a/tdrive/backend/node/src/core/platform/services/storage/storage-service.ts +++ b/tdrive/backend/node/src/core/platform/services/storage/storage-service.ts @@ -77,6 +77,10 @@ export default class StorageService extends TdriveService implements return this.getConnector().exists(path + "/chunk1", options); } + enumeratePathsForFile(filePath: string): Promise { + return this.getConnector().enumeratePathsForFile(filePath); + } + async write(path: string, stream: Stream, options?: WriteOptions): Promise { try { if (options?.encryptionKey) {