diff --git a/packages/providers/interface/src/provider.ts b/packages/providers/interface/src/provider.ts index a151cc9..ac1fc70 100644 --- a/packages/providers/interface/src/provider.ts +++ b/packages/providers/interface/src/provider.ts @@ -12,7 +12,12 @@ export abstract class StorageServiceProvider { expiresIn: ExpiresIn ): SignedUploadUrlRequest - abstract getData(fileId: ID): GetDataRequest + /** + * + * @param fileId + * @param optimistic If true, the provider will return the data without checking the existence of the file + */ + abstract getData(fileId: ID, optimistic?: boolean): GetDataRequest abstract delete(fileId: ID): DeleteRequest } diff --git a/packages/providers/s3/__test__/s3-provider-configuration-parser.test.ts b/packages/providers/s3/__test__/s3-provider-configuration-parser.test.ts index 24f3418..b6b98b4 100644 --- a/packages/providers/s3/__test__/s3-provider-configuration-parser.test.ts +++ b/packages/providers/s3/__test__/s3-provider-configuration-parser.test.ts @@ -28,7 +28,6 @@ describe('S3ProviderConfigurationParser', () => { bucketPath: [bareMinimumConfig.bucketPath], }, acl: ObjectCannedACL.bucket_owner_full_control, - optimisticFileDataResponse: true, }) }) diff --git a/packages/providers/s3/src/provider.ts b/packages/providers/s3/src/provider.ts index 563ac38..406c412 100644 --- a/packages/providers/s3/src/provider.ts +++ b/packages/providers/s3/src/provider.ts @@ -97,42 +97,43 @@ export class S3Provider extends StorageServiceProvider { } } - private async createFileUrls(fileId: ID): Promise { - if (!this.configuration.optimisticFileDataResponse) { - const existingKeys = await this.buckets.resource.existingKeys( - fileId - ) + private async createFileUrls( + fileId: ID, + optimistic: boolean + ): Promise { + if (optimistic) { + return this.buckets.resource.getSignedDownloadUrls(fileId) + } - if (existingKeys.length === 0) { - return [] - } else { - return this.buckets.resource.getSignedDownloadUrls( - fileId, - existingKeys as unknown as S3ResourceBucketPath - ) - } + const existingKeys = await this.buckets.resource.existingKeys(fileId) + + if (existingKeys.length > 0) { + return this.buckets.resource.getSignedDownloadUrls( + fileId, + existingKeys as unknown as S3ResourceBucketPath + ) } - return this.buckets.resource.getSignedDownloadUrls(fileId) + return [] } - async getData(fileId: ID): GetDataRequest { + async getData(fileId: ID, optimistic = false): GetDataRequest { // TODO: Refactor the file check - const variants = await this.createFileUrls(fileId) + const variants = await this.createFileUrls(fileId, optimistic) - if (variants.length === 0) { + if ( + variants.length === + this.configuration.resourceBucket.bucketPath.length + ) { return { id: fileId, - variants: [], - status: FileStatus.NOT_FOUND, + variants, + status: FileStatus.PROCESSED, } } - if ( - variants.length !== - this.configuration.resourceBucket.bucketPath.length - ) { + if (variants.length > 0) { return { id: fileId, variants, @@ -140,10 +141,24 @@ export class S3Provider extends StorageServiceProvider { } } + if (!optimistic && !this.buckets.upload.equals(this.buckets.resource)) { + const existsInUploadBucket = await this.buckets.upload.keyExists( + this.buckets.upload.keyResolver.resolve(fileId) + ) + + if (existsInUploadBucket) { + return { + id: fileId, + variants: [], + status: FileStatus.UPLOADED, + } + } + } + return { id: fileId, - variants, - status: FileStatus.PROCESSED, + variants: [], + status: FileStatus.NOT_FOUND, } } diff --git a/packages/providers/s3/src/types.ts b/packages/providers/s3/src/types.ts index 4b4dd2b..c8ac37f 100644 --- a/packages/providers/s3/src/types.ts +++ b/packages/providers/s3/src/types.ts @@ -93,15 +93,6 @@ export interface S3ProviderConfiguration extends S3DefaultBucketConfiguration { * @see https://docs.aws.amazon.com/AmazonS3/latest/userguide/acl-overview.html#canned-acl */ acl?: ObjectCannedACL - /** - * Whether the file response should be optimistic or not. - * - * @default true - * @description If set to false it will make an additional request to the S3 API to check if the file exists. - * If the file does not exist, the `status` property of the response will be set to `NOT_FOUND`. - * If the file does exist, the `status` property of the response will be set to `PROCESSED`. - */ - optimisticFileDataResponse?: boolean } export interface S3ProviderConfigurationParsed diff --git a/packages/providers/s3/src/utils/s3-bucket.ts b/packages/providers/s3/src/utils/s3-bucket.ts index 8cb4162..af42b90 100644 --- a/packages/providers/s3/src/utils/s3-bucket.ts +++ b/packages/providers/s3/src/utils/s3-bucket.ts @@ -18,7 +18,7 @@ import { import { ExpiresIn } from 'shared-types' import { getSignedUrl } from '@aws-sdk/s3-request-presigner' -class S3Bucket< +abstract class S3Bucket< Config extends | Required | Required, @@ -42,6 +42,12 @@ class S3Bucket< this.path = s3Config.bucketPath } + abstract equals( + bucket: this extends S3UploadBucket + ? S3ResourceBucket + : S3UploadBucket + ): boolean + async keyExists( key: string, commandInput?: Omit @@ -72,6 +78,14 @@ export class S3UploadBucket extends S3Bucket< Required, ID > { + equals(bucket: S3ResourceBucket): boolean { + return ( + this.name === bucket.name && + this.region === bucket.region && + this.path === bucket.path[0] + ) + } + async getSignedUploadUrl( fileId: ID, expiresIn: ExpiresIn, @@ -93,6 +107,14 @@ export class S3ResourceBucket extends S3Bucket< Required, ID > { + equals(bucket: S3UploadBucket): boolean { + return ( + this.name === bucket.name && + this.region === bucket.region && + this.path[0] === bucket.path + ) + } + async getSignedDownloadUrl( key: string, commandInput?: Omit diff --git a/packages/providers/s3/src/utils/s3-provider-configuration-parser.ts b/packages/providers/s3/src/utils/s3-provider-configuration-parser.ts index 46dde5a..1a79ef3 100644 --- a/packages/providers/s3/src/utils/s3-provider-configuration-parser.ts +++ b/packages/providers/s3/src/utils/s3-provider-configuration-parser.ts @@ -43,7 +43,6 @@ export class S3ProviderConfigurationParser { bucketPath: z.array(z.string()).nonempty(), }), acl: z.nativeEnum(ObjectCannedACL), - optimisticFileDataResponse: z.boolean(), } ) @@ -86,8 +85,6 @@ export class S3ProviderConfigurationParser { ...s3DefaultBucketConfiguration, resourceBucket: s3DefaultResourceBucketConfiguration, acl: configuration.acl ?? ObjectCannedACL.bucket_owner_full_control, - optimisticFileDataResponse: - configuration.optimisticFileDataResponse ?? true, } } } diff --git a/packages/server/core/src/upload-wizard.ts b/packages/server/core/src/upload-wizard.ts index 1c67431..363e5b5 100644 --- a/packages/server/core/src/upload-wizard.ts +++ b/packages/server/core/src/upload-wizard.ts @@ -76,7 +76,8 @@ export class UploadWizard { async getData(fileId: ID): Promise> { const { status, variants } = await this.storageServiceProvider.getData( - fileId + fileId, + true ) if (status === FileStatus.NOT_FOUND) {