From 287d0e5157363eccbc498287cc2f0011ecb81918 Mon Sep 17 00:00:00 2001 From: AllanZhengYP Date: Wed, 2 Oct 2024 13:44:14 -0700 Subject: [PATCH] feat(storage): internal GetProperties API (#13869) --------- Co-authored-by: Jim Blanchard --- .../internals/apis/getProperties.test.ts | 57 +++++++++++++++++++ .../src/internals/apis/getProperties.ts | 33 +++++++++++ packages/storage/src/internals/index.ts | 6 +- .../storage/src/internals/types/inputs.ts | 56 ++++++++++++++++++ .../storage/src/internals/types/outputs.ts | 7 +++ .../src/providers/s3/apis/internal/copy.ts | 6 +- .../s3/apis/internal/getProperties.ts | 5 +- .../storage/src/providers/s3/types/inputs.ts | 8 +-- .../storage/src/providers/s3/types/options.ts | 14 ++--- 9 files changed, 172 insertions(+), 20 deletions(-) create mode 100644 packages/storage/__tests__/internals/apis/getProperties.test.ts create mode 100644 packages/storage/src/internals/apis/getProperties.ts diff --git a/packages/storage/__tests__/internals/apis/getProperties.test.ts b/packages/storage/__tests__/internals/apis/getProperties.test.ts new file mode 100644 index 00000000000..4b58d4cc8e1 --- /dev/null +++ b/packages/storage/__tests__/internals/apis/getProperties.test.ts @@ -0,0 +1,57 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { AmplifyClassV6 } from '@aws-amplify/core'; + +import { getProperties as advancedGetProperties } from '../../../src/internals'; +import { getProperties as getPropertiesInternal } from '../../../src/providers/s3/apis/internal/getProperties'; + +jest.mock('../../../src/providers/s3/apis/internal/getProperties'); +const mockedGetPropertiesInternal = jest.mocked(getPropertiesInternal); + +describe('getProperties (internal)', () => { + beforeEach(() => { + mockedGetPropertiesInternal.mockResolvedValue({ + path: 'output/path/to/mock/object', + }); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + it('should pass advanced option locationCredentialsProvider to internal getProperties', async () => { + const useAccelerateEndpoint = true; + const bucket = { bucketName: 'bucket', region: 'us-east-1' }; + const locationCredentialsProvider = async () => ({ + credentials: { + accessKeyId: 'akid', + secretAccessKey: 'secret', + sessionToken: 'token', + expiration: new Date(), + }, + }); + const result = await advancedGetProperties({ + path: 'input/path/to/mock/object', + options: { + useAccelerateEndpoint, + bucket, + locationCredentialsProvider, + }, + }); + expect(mockedGetPropertiesInternal).toHaveBeenCalledTimes(1); + expect(mockedGetPropertiesInternal).toHaveBeenCalledWith( + expect.any(AmplifyClassV6), + { + path: 'input/path/to/mock/object', + options: { + useAccelerateEndpoint, + bucket, + locationCredentialsProvider, + }, + }, + ); + expect(result).toEqual({ + path: 'output/path/to/mock/object', + }); + }); +}); diff --git a/packages/storage/src/internals/apis/getProperties.ts b/packages/storage/src/internals/apis/getProperties.ts new file mode 100644 index 00000000000..d1050c4c1a2 --- /dev/null +++ b/packages/storage/src/internals/apis/getProperties.ts @@ -0,0 +1,33 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { Amplify } from '@aws-amplify/core'; + +import { getProperties as getPropertiesInternal } from '../../providers/s3/apis/internal/getProperties'; +import { GetPropertiesInput } from '../types/inputs'; +import { GetPropertiesOutput } from '../types/outputs'; + +/** + * Gets the properties of a file. The properties include S3 system metadata and + * the user metadata that was provided when uploading the file. + * @param input - The `GetPropertiesWithPathInput` object. + * @returns Requested object properties. + * @throws An `S3Exception` when the underlying S3 service returned error. + * @throws A `StorageValidationErrorCode` when API call parameters are invalid. + * + * @internal + */ +export function getProperties( + input: GetPropertiesInput, +): Promise { + return getPropertiesInternal(Amplify, { + path: input.path, + options: { + useAccelerateEndpoint: input?.options?.useAccelerateEndpoint, + bucket: input?.options?.bucket, + locationCredentialsProvider: input?.options?.locationCredentialsProvider, + }, + // Type casting is necessary because `getPropertiesInternal` supports both Gen1 and Gen2 signatures, but here + // given in input can only be Gen2 signature, the return can only ben Gen2 signature. + }) as Promise; +} diff --git a/packages/storage/src/internals/index.ts b/packages/storage/src/internals/index.ts index 279ef2afd3b..6ca2bdba6dd 100644 --- a/packages/storage/src/internals/index.ts +++ b/packages/storage/src/internals/index.ts @@ -1,7 +1,6 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -export { LocationCredentialsProvider } from '../providers/s3/types/options'; export { StorageSubpathStrategy } from '../types/options'; export { Permission } from './types/common'; @@ -12,14 +11,18 @@ Internal APIs export { GetDataAccessInput, ListCallerAccessGrantsInput, + GetPropertiesInput, + CopyInput, } from './types/inputs'; export { GetDataAccessOutput, ListCallerAccessGrantsOutput, + GetPropertiesOutput, } from './types/outputs'; export { getDataAccess } from './apis/getDataAccess'; export { listCallerAccessGrants } from './apis/listCallerAccessGrants'; +export { getProperties } from './apis/getProperties'; /* CredentialsStore exports @@ -41,4 +44,5 @@ export { GetLocationCredentialsInput, GetLocationCredentialsOutput, } from './types/credentials'; + export { AWSTemporaryCredentials } from '../providers/s3/types/options'; diff --git a/packages/storage/src/internals/types/inputs.ts b/packages/storage/src/internals/types/inputs.ts index 51d04acf9c6..8e813c28e4a 100644 --- a/packages/storage/src/internals/types/inputs.ts +++ b/packages/storage/src/internals/types/inputs.ts @@ -1,6 +1,16 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 +import { + StorageCopyInputWithPath, + StorageOperationInputWithPath, + StorageOperationOptionsInput, +} from '../../types/inputs'; +import { + CopyWithPathInput, + GetPropertiesWithPathInput, +} from '../../providers/s3'; + import { CredentialsProvider, ListLocationsInput } from './credentials'; import { Permission, PrefixType, Privilege } from './common'; @@ -26,3 +36,49 @@ export interface GetDataAccessInput { region: string; scope: string; } + +/** + * @internal + */ +export type GetPropertiesInput = ExtendInputWithAdvancedOptions< + GetPropertiesWithPathInput, + { + locationCredentialsProvider?: CredentialsProvider; + } +>; + +/** + * @internal + */ +export type CopyInput = ExtendCopyInputWithAdvancedOptions< + CopyWithPathInput, + { + locationCredentialsProvider?: CredentialsProvider; + } +>; + +/** + * Generic types that extend the public non-copy API input types with extended + * options. This is a temporary solution to support advanced options from internal APIs. + */ +type ExtendInputWithAdvancedOptions = + InputType extends StorageOperationInputWithPath & + StorageOperationOptionsInput + ? { + path: InputType['path']; + options?: PublicInputOptionsType & ExtendedOptionsType; + } + : never; + +/** + * Generic types that extend the public copy API input type with extended options. + * This is a temporary solution to support advanced options from internal APIs. + */ +type ExtendCopyInputWithAdvancedOptions = + InputType extends StorageCopyInputWithPath + ? { + source: InputType['source']; + destination: InputType['destination']; + options?: ExtendedOptionsType; + } + : never; diff --git a/packages/storage/src/internals/types/outputs.ts b/packages/storage/src/internals/types/outputs.ts index 09eecb7cfc2..0a47381ff69 100644 --- a/packages/storage/src/internals/types/outputs.ts +++ b/packages/storage/src/internals/types/outputs.ts @@ -1,6 +1,8 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 +import { GetPropertiesWithPathOutput } from '../../providers/s3/types'; + import { ListLocationsOutput, LocationCredentials } from './credentials'; /** @@ -12,3 +14,8 @@ export type ListCallerAccessGrantsOutput = ListLocationsOutput; * @internal */ export type GetDataAccessOutput = LocationCredentials; + +/** + * @internal + */ +export type GetPropertiesOutput = GetPropertiesWithPathOutput; diff --git a/packages/storage/src/providers/s3/apis/internal/copy.ts b/packages/storage/src/providers/s3/apis/internal/copy.ts index b86ae3e67ff..30d4bd136c4 100644 --- a/packages/storage/src/providers/s3/apis/internal/copy.ts +++ b/packages/storage/src/providers/s3/apis/internal/copy.ts @@ -21,6 +21,8 @@ import { assertValidationError } from '../../../../errors/utils/assertValidation import { copyObject } from '../../utils/client/s3data'; import { getStorageUserAgentValue } from '../../utils/userAgent'; import { logger } from '../../../../utils'; +// TODO: Remove this interface when we move to public advanced APIs. +import { CopyInput as CopyWithPathInputWithAdvancedOptions } from '../../../../internals'; const isCopyInputWithPath = ( input: CopyInput | CopyWithPathInput, @@ -44,7 +46,7 @@ const storageBucketAssertion = ( export const copy = async ( amplify: AmplifyClassV6, - input: CopyInput | CopyWithPathInput, + input: CopyInput | CopyWithPathInputWithAdvancedOptions, ): Promise => { return isCopyInputWithPath(input) ? copyWithPath(amplify, input) @@ -53,7 +55,7 @@ export const copy = async ( const copyWithPath = async ( amplify: AmplifyClassV6, - input: CopyWithPathInput, + input: CopyWithPathInputWithAdvancedOptions, ): Promise => { const { source, destination } = input; diff --git a/packages/storage/src/providers/s3/apis/internal/getProperties.ts b/packages/storage/src/providers/s3/apis/internal/getProperties.ts index ac04b2dbe6e..e07d721c989 100644 --- a/packages/storage/src/providers/s3/apis/internal/getProperties.ts +++ b/packages/storage/src/providers/s3/apis/internal/getProperties.ts @@ -7,7 +7,6 @@ import { StorageAction } from '@aws-amplify/core/internals/utils'; import { GetPropertiesInput, GetPropertiesOutput, - GetPropertiesWithPathInput, GetPropertiesWithPathOutput, } from '../../types'; import { @@ -18,10 +17,12 @@ import { headObject } from '../../utils/client/s3data'; import { getStorageUserAgentValue } from '../../utils/userAgent'; import { logger } from '../../../../utils'; import { STORAGE_INPUT_KEY } from '../../utils/constants'; +// TODO: Remove this interface when we move to public advanced APIs. +import { GetPropertiesInput as GetPropertiesWithPathInputWithAdvancedOptions } from '../../../../internals'; export const getProperties = async ( amplify: AmplifyClassV6, - input: GetPropertiesInput | GetPropertiesWithPathInput, + input: GetPropertiesInput | GetPropertiesWithPathInputWithAdvancedOptions, action?: StorageAction, ): Promise => { const { s3Config, bucket, keyPrefix, identityId } = diff --git a/packages/storage/src/providers/s3/types/inputs.ts b/packages/storage/src/providers/s3/types/inputs.ts index 041451fbfd5..7b405964fb6 100644 --- a/packages/storage/src/providers/s3/types/inputs.ts +++ b/packages/storage/src/providers/s3/types/inputs.ts @@ -17,7 +17,6 @@ import { StorageUploadDataInputWithKey, StorageUploadDataInputWithPath, } from '../../../types'; -import { StorageOperationOptionsInput } from '../../../types/inputs'; import { CopyDestinationWithKeyOptions, CopySourceWithKeyOptions, @@ -36,8 +35,6 @@ import { UploadDataWithPathOptions, } from '../types'; -import { LocationCredentialsProvider } from './options'; - // TODO: support use accelerate endpoint option /** * @deprecated Use {@link CopyWithPathInput} instead. @@ -50,10 +47,7 @@ export type CopyInput = StorageCopyInputWithKey< /** * Input type with path for S3 copy API. */ -export type CopyWithPathInput = StorageCopyInputWithPath & - StorageOperationOptionsInput<{ - locationCredentialsProvider?: LocationCredentialsProvider; - }>; +export type CopyWithPathInput = StorageCopyInputWithPath; /** * @deprecated Use {@link GetPropertiesWithPathInput} instead. diff --git a/packages/storage/src/providers/s3/types/options.ts b/packages/storage/src/providers/s3/types/options.ts index f181800408c..3442b6867cc 100644 --- a/packages/storage/src/providers/s3/types/options.ts +++ b/packages/storage/src/providers/s3/types/options.ts @@ -2,11 +2,11 @@ // SPDX-License-Identifier: Apache-2.0 import { StorageAccessLevel } from '@aws-amplify/core'; -import { AWSCredentials } from '@aws-amplify/core/internals/utils'; import { CredentialsProviderOptions, SigningOptions, } from '@aws-amplify/core/internals/aws-client-utils'; +import { AWSCredentials } from '@aws-amplify/core/internals/utils'; import { TransferProgressEvent } from '../../../types'; import { @@ -26,6 +26,11 @@ export type AWSTemporaryCredentials = Required< >; /** + * Async function returning AWS credentials for an API call. This function + * is invoked with S3 locations(bucket and path). + * If omitted, the global credentials configured in Amplify Auth + * would be used. + * * @internal */ export type LocationCredentialsProvider = ( @@ -45,13 +50,6 @@ interface CommonOptions { */ useAccelerateEndpoint?: boolean; - /** - * Async function returning AWS credentials for an API call. This function - * is invoked with S3 locations(bucket and path). - * If omitted, the global credentials configured in Amplify Auth - * would be used. - */ - locationCredentialsProvider?: LocationCredentialsProvider; bucket?: StorageBucket; }