Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Poc/storge gen2 #1

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
55 changes: 40 additions & 15 deletions packages/storage/src/providers/s3/apis/downloadData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,18 @@
import { Amplify } from '@aws-amplify/core';
import { StorageAction } from '@aws-amplify/core/internals/utils';

import { DownloadDataInput, DownloadDataOutput, S3Exception } from '../types';
import { DownloadDataInput, DownloadDataOutput } from '../types';
import { resolveS3ConfigAndInput } from '../utils/resolveS3ConfigAndInput';
import { StorageValidationErrorCode } from '../../../errors/types/validation';
import { createDownloadTask } from '../utils';
import { getObject } from '../utils/client';
import { getStorageUserAgentValue } from '../utils/userAgent';
import { logger } from '../../../utils';
import { DownloadDataOutputPath } from '../types/outputs';
import { StorageDownloadDataOutput } from '../../../types';
import { DownloadDataInputKey, DownloadDataInputPath } from '../types/inputs';
import { validateStorageOperationInput } from '../utils/utils';
import { STORAGE_INPUT_TYPES } from '../utils/constants';
import { StorageItem, StorageItemPath } from '../../../types/outputs';

/**
* Download S3 object data to memory
Expand All @@ -19,8 +24,8 @@
* @returns A cancelable task exposing result promise from `result` property.
* @throws service: {@link S3Exception} - thrown when checking for existence of the object
* @throws validation: {@link StorageValidationErrorCode } - Validation errors
*

Check warning on line 27 in packages/storage/src/providers/s3/apis/downloadData.ts

View workflow job for this annotation

GitHub Actions / unit-tests / Unit Test - @aws-amplify/storage

The type 'S3Exception' is undefined
* @example

Check warning on line 28 in packages/storage/src/providers/s3/apis/downloadData.ts

View workflow job for this annotation

GitHub Actions / unit-tests / Unit Test - @aws-amplify/storage

The type 'StorageValidationErrorCode' is undefined
* ```ts
* // Download a file from s3 bucket
* const { body, eTag } = await downloadData({ key, data: file, options: {
Expand All @@ -42,32 +47,49 @@
* }
*```
*/
export const downloadData = (input: DownloadDataInput): DownloadDataOutput => {

interface DownloadData {
(input: DownloadDataInputPath): DownloadDataOutputPath;
(input: DownloadDataInputKey): DownloadDataOutput;
}

export const downloadData: DownloadData = <
Output extends DownloadDataOutput | DownloadDataOutputPath,
>(
input: DownloadDataInput,
): Output => {
const abortController = new AbortController();

const downloadTask = createDownloadTask({
job: downloadDataJob(input, abortController.signal),
job: downloadDataJob(input as DownloadDataInputKey, abortController.signal),
onCancel: (message?: string) => {
abortController.abort(message);
},
});

return downloadTask;
return downloadTask as Output;
};

const downloadDataJob =
(
{ options: downloadDataOptions, key }: DownloadDataInput,
<DownloadDataInput extends DownloadDataInputKey | DownloadDataInputPath>(
downloadDataInput: DownloadDataInput,
abortSignal: AbortSignal,
) =>
async () => {
const { bucket, keyPrefix, s3Config } = await resolveS3ConfigAndInput(
Amplify,
downloadDataOptions,
async (): Promise<
StorageDownloadDataOutput<StorageItem | StorageItemPath>
> => {
const { options: downloadDataOptions } = downloadDataInput;
const { bucket, keyPrefix, s3Config, identityId, userSub } =
await resolveS3ConfigAndInput(Amplify, downloadDataOptions);
const { inputType, objectKey } = validateStorageOperationInput(
downloadDataInput,
identityId,
userSub,
);
const finalKey = keyPrefix + key;
const finalKey =
inputType === STORAGE_INPUT_TYPES.KEY ? keyPrefix + objectKey : objectKey;

logger.debug(`download ${key} from ${finalKey}.`);
logger.debug(`download ${objectKey} from ${finalKey}.`);

const {
Body: body,
Expand All @@ -93,8 +115,7 @@
},
);

return {
key,
const result = {
body,
lastModified,
size,
Expand All @@ -103,4 +124,8 @@
metadata,
versionId,
};

return inputType === STORAGE_INPUT_TYPES.KEY
? { key: objectKey, ...result }
: { path: finalKey, ...result };
};
17 changes: 14 additions & 3 deletions packages/storage/src/providers/s3/apis/uploadData/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { createUploadTask } from '../../utils';
import { assertValidationError } from '../../../../errors/utils/assertValidationError';
import { StorageValidationErrorCode } from '../../../../errors/types/validation';
import { DEFAULT_PART_SIZE, MAX_OBJECT_SIZE } from '../../utils/constants';
import { UploadDataInputKey, UploadDataInputPath } from '../../types/inputs';
import { UploadDataOutputPath } from '../../types/outputs';

import { byteLength } from './byteLength';
import { putObjectJob } from './putObjectJob';
Expand Down Expand Up @@ -59,7 +61,16 @@ import { getMultipartUploadHandlers } from './multipart';
* await uploadTask.result;
* ```
*/
export const uploadData = (input: UploadDataInput): UploadDataOutput => {
interface UploadData {
(input: UploadDataInputKey): UploadDataOutput;
(input: UploadDataInputPath): UploadDataOutputPath;
}

export const uploadData: UploadData = <
Output extends UploadDataOutput | UploadDataOutputPath,
>(
input: UploadDataInput,
): Output => {
const { data } = input;

const dataByteLength = byteLength(data);
Expand All @@ -77,7 +88,7 @@ export const uploadData = (input: UploadDataInput): UploadDataOutput => {
onCancel: (message?: string) => {
abortController.abort(message);
},
});
}) as Output;
} else {
const { multipartUploadJob, onPause, onResume, onCancel } =
getMultipartUploadHandlers(input, dataByteLength);
Expand All @@ -90,6 +101,6 @@ export const uploadData = (input: UploadDataInput): UploadDataOutput => {
},
onPause,
onResume,
});
}) as Output;
}
};
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import { getDataChunker } from './getDataChunker';
* @internal
*/
export const getMultipartUploadHandlers = (
{ options: uploadDataOptions, key, data }: UploadDataInput,
{ options: uploadDataOptions, key = '', data }: UploadDataInput,
size?: number,
) => {
let resolveCallback: ((value: S3Item) => void) | undefined;
Expand Down Expand Up @@ -70,12 +70,12 @@ export const getMultipartUploadHandlers = (
abortController = new AbortController();
isAbortSignalFromPause = false;

const accessLevel = 'guest';
const {
contentDisposition,
contentEncoding,
contentType = 'application/octet-stream',
metadata,
accessLevel,
onProgress,
} = uploadDataOptions ?? {};

Expand Down Expand Up @@ -104,7 +104,7 @@ export const getMultipartUploadHandlers = (
uploadCacheKey = size
? getUploadsCacheKey({
file: data instanceof File ? data : undefined,
accessLevel: resolveAccessLevel(uploadDataOptions?.accessLevel),
accessLevel: resolveAccessLevel(accessLevel),
contentType: uploadDataOptions?.contentType,
bucket: resolvedBucket!,
size,
Expand Down
39 changes: 30 additions & 9 deletions packages/storage/src/providers/s3/apis/uploadData/putObjectJob.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@
import { Amplify } from '@aws-amplify/core';
import { StorageAction } from '@aws-amplify/core/internals/utils';

import { UploadDataInput } from '../../types';
import { calculateContentMd5, resolveS3ConfigAndInput } from '../../utils';
import { Item as S3Item } from '../../types/outputs';
import { Item as S3Item, ItemPath as S3ItemPath } from '../../types/outputs';
import { putObject } from '../../utils/client';
import { getStorageUserAgentValue } from '../../utils/userAgent';
import { UploadDataInput } from '../../types/inputs';
import { validateStorageOperationInput } from '../../utils/utils';
import { STORAGE_INPUT_TYPES } from '../../utils/constants';

/**
* Get a function the returns a promise to call putObject API to S3.
Expand All @@ -17,15 +19,31 @@ import { getStorageUserAgentValue } from '../../utils/userAgent';
*/
export const putObjectJob =
(
{ options: uploadDataOptions, key, data }: UploadDataInput,
uploadInput: UploadDataInput,
abortSignal: AbortSignal,
totalLength?: number,
) =>
async (): Promise<S3Item> => {
const { bucket, keyPrefix, s3Config, isObjectLockEnabled } =
await resolveS3ConfigAndInput(Amplify, uploadDataOptions);
async (): Promise<S3Item | S3ItemPath> => {
const { options: uploadDataOptions, data } = uploadInput;

const {
bucket,
keyPrefix,
s3Config,
isObjectLockEnabled,
identityId,
userSub,
} = await resolveS3ConfigAndInput(Amplify, uploadDataOptions);

const { inputType, objectKey } = validateStorageOperationInput(
uploadInput,
identityId,
userSub,
);

const finalKey =
inputType === STORAGE_INPUT_TYPES.KEY ? keyPrefix + objectKey : objectKey;

const finalKey = keyPrefix + key;
const {
contentDisposition,
contentEncoding,
Expand Down Expand Up @@ -55,12 +73,15 @@ export const putObjectJob =
},
);

return {
key,
const result = {
eTag,
versionId,
contentType,
metadata,
size: totalLength,
};

return inputType === STORAGE_INPUT_TYPES.KEY
? { key: objectKey, ...result }
: { path: finalKey, ...result };
};
33 changes: 33 additions & 0 deletions packages/storage/src/providers/s3/apis/uploadData/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { UploadDataInput } from '../../types';
import { UploadDataInputKey, UploadDataInputPath } from '../../types/inputs';

const isInputWithKey = (
input: UploadDataInput,
): input is UploadDataInputKey => {
return input.key !== undefined;
};

const isInputWithPath = (
input: UploadDataInput,
): input is UploadDataInputPath => {
return input.path !== undefined;
};

export const validateUploadInput = (
uploadInput: UploadDataInput,
keyPrefix: string,
) => {
let finalKey;
if (isInputWithPath(uploadInput)) {
const { path } = uploadInput;
finalKey = typeof path === 'string' ? path : path({});

return { inputType: 'path', finalKey };
} else if (isInputWithKey(uploadInput)) {
finalKey = keyPrefix + uploadInput.key;

return { inputType: 'key', finalKey };
} else {
throw new Error('invalid input');
}
};
29 changes: 27 additions & 2 deletions packages/storage/src/providers/s3/types/inputs.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

import { StrictUnion } from '@aws-amplify/core/internals/utils';

import {
StorageCopyInput,
StorageDownloadDataInput,
StorageDownloadDataInputPath,
StorageGetPropertiesInput,
StorageGetUrlInput,
StorageListInput,
StorageRemoveInput,
StorageUploadDataInput,
StorageUploadDataInputPath,
} from '../../../types';
import {
CopyDestinationOptions,
Expand All @@ -22,6 +26,8 @@ import {
UploadDataOptions,
} from '../types';

import { DownloadDataOptionsPath, UploadDataOptionsNew } from './options';

// TODO: support use accelerate endpoint option
/**
* Input type for S3 copy API.
Expand Down Expand Up @@ -60,9 +66,28 @@ export type RemoveInput = StorageRemoveInput<RemoveOptions>;
/**
* Input type for S3 downloadData API.
*/
export type DownloadDataInput = StorageDownloadDataInput<DownloadDataOptions>;
export type DownloadDataInputKey =
StorageDownloadDataInput<DownloadDataOptions>;
export type DownloadDataInputPath =
StorageDownloadDataInputPath<DownloadDataOptionsPath>;
export type DownloadDataInput = StrictUnion<
DownloadDataInputKey | DownloadDataInputPath
>;

/**
* Input type for S3 uploadData API with key.
*/
export type UploadDataInputKey = StorageUploadDataInput<UploadDataOptions>;

/**
* Input type for S3 uploadData API with path.
*/
export type UploadDataInputPath =
StorageUploadDataInputPath<UploadDataOptionsNew>;

/**
* Input type for S3 uploadData API.
*/
export type UploadDataInput = StorageUploadDataInput<UploadDataOptions>;
export type UploadDataInput = StrictUnion<
UploadDataInputKey | UploadDataInputPath
>;
28 changes: 28 additions & 0 deletions packages/storage/src/providers/s3/types/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,10 @@ export type DownloadDataOptions = ReadOptions &
TransferOptions &
BytesRangeOptions;

export type DownloadDataOptionsPath = CommonOptions &
TransferOptions &
BytesRangeOptions;

export type UploadDataOptions = WriteOptions &
CommonOptions &
TransferOptions & {
Expand All @@ -117,6 +121,30 @@ export type UploadDataOptions = WriteOptions &
metadata?: Record<string, string>;
};

export type UploadDataOptionsNew = CommonOptions &
TransferOptions & {
/**
* The default content-disposition header value of the file when downloading it.
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition
*/
contentDisposition?: string;
/**
* The default content-encoding header value of the file when downloading it.
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Encoding
*/
contentEncoding?: string;
/**
* The default content-type header value of the file when downloading it.
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type
*/
contentType?: string;
/**
* The user-defined metadata for the object uploaded to S3.
* @see https://docs.aws.amazon.com/AmazonS3/latest/userguide/UsingMetadata.html#UserMetadata
*/
metadata?: Record<string, string>;
};

export type CopySourceOptions = ReadOptions & {
key: string;
};
Expand Down
Loading
Loading