Skip to content

Commit

Permalink
Merge branch 'next' into fix-auth-config-one
Browse files Browse the repository at this point in the history
  • Loading branch information
elorzafe authored Aug 29, 2023
2 parents 832a5ce + 6fd4a4f commit a6ccae1
Show file tree
Hide file tree
Showing 17 changed files with 98 additions and 69 deletions.
4 changes: 2 additions & 2 deletions packages/auth/__tests__/providers/cognito/signOut.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ describe('signOut tests no oauth request fail', () => {
Auth: {
tokenProvider: TokenProvider.CognitoUserPoolsTokenProvider,
credentialsProvider: {
clearCredentials() {
clearCredentialsAndIdentityId() {
clearCredentialsSpy();
},
getCredentialsAndIdentityId(getCredentialsOptions) {
Expand Down Expand Up @@ -250,7 +250,7 @@ describe('signOut tests with oauth', () => {
Auth: {
tokenProvider: TokenProvider.CognitoUserPoolsTokenProvider,
credentialsProvider: {
clearCredentials() {
clearCredentialsAndIdentityId() {
clearCredentialsSpy();
},
getCredentialsAndIdentityId(getCredentialsOptions) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,14 @@ export class CognitoAWSCredentialsAndIdentityIdProvider
}

// TODO(V6): export clear crecentials to singleton
async clearCredentialsAndIdentityId(): Promise<void> {
logger.debug('Clearing out credentials and identityId');
this._credentialsAndIdentityId = undefined;
await this._identityIdStore.clearIdentityId();
}

async clearCredentials(): Promise<void> {
logger.debug('Clearing out credentials');
logger.debug('Clearing out in-memory credentials');
this._credentialsAndIdentityId = undefined;
}

Expand Down Expand Up @@ -90,9 +96,6 @@ export class CognitoAWSCredentialsAndIdentityIdProvider
this.clearCredentials();
}

// check eligibility for guest credentials
// - if there is error fetching tokens
// - if user is not signed in
if (!isAuthenticated) {
return await this.getGuestCredentials(identityId, authConfig.Cognito);
} else {
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/singleton/Auth/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ export class AuthClass {

async clearCredentials(): Promise<void> {
if (this.authOptions?.credentialsProvider) {
return await this.authOptions.credentialsProvider.clearCredentials();
return await this.authOptions.credentialsProvider.clearCredentialsAndIdentityId();
}
}
}
2 changes: 1 addition & 1 deletion packages/core/src/singleton/Auth/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export interface AWSCredentialsAndIdentityIdProvider {
getCredentialsAndIdentityId: (
getCredentialsOptions: GetCredentialsOptions
) => Promise<AWSCredentialsAndIdentityId>;
clearCredentials: () => void;
clearCredentialsAndIdentityId: () => void;
}

export type TokenProvider = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

import {
xhrTransferHandler,
isCancelError,
} from '../../src/AwsClients/S3/runtime/xhrTransferHandler';
import { xhrTransferHandler } from '../../src/AwsClients/S3/runtime/xhrTransferHandler';
import { isCancelError } from '../../src/errors/CanceledError';
import {
spyOnXhr,
mockXhrResponse,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
import { UPLOADS_STORAGE_KEY } from '../../../../src/common/StorageConstants';
import { getKvStorage } from '../../../../src/providers/s3/apis/uploadData/multipart/uploadCache/kvStorage';
import { byteLength } from '../../../../src/providers/s3/apis/uploadData/byteLength';
import { CanceledError } from '../../../../src/errors/CanceledError';

jest.mock('../../../../src/AwsClients/S3');

Expand Down Expand Up @@ -523,8 +524,8 @@ describe('getMultipartUploadHandlers', () => {
await multipartUploadJob();
fail('should throw error');
} catch (error) {
expect(error).toBeInstanceOf(Error);
expect(error.message).toBe('AbortError');
expect(error).toBeInstanceOf(CanceledError);
expect(error.message).toBe('Upload is canceled by user');
}
expect(mockAbortMultipartUpload).toBeCalledTimes(1);
expect(mockUploadPart).toBeCalledTimes(2);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,4 @@ export {
} from './constants';
export { s3TransferHandler } from './s3TransferHandler/xhr';
export { parser } from './xmlParser/dom';
export { isCancelError } from './xhrTransferHandler';
export { toBase64, utf8Encode } from './base64/index.browser';
1 change: 0 additions & 1 deletion packages/storage/src/AwsClients/S3/runtime/index.native.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,4 @@ export {
} from './constants';
export { s3TransferHandler } from './s3TransferHandler/xhr';
export { parser } from './xmlParser/pureJs';
export { isCancelError } from './xhrTransferHandler';
export { toBase64, utf8Encode } from './base64/index.native';
2 changes: 0 additions & 2 deletions packages/storage/src/AwsClients/S3/runtime/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ export {
} from './constants';
export { s3TransferHandler } from './s3TransferHandler/fetch';
export { parser } from './xmlParser/pureJs';
// TODO[AllanZhengYP]: support isCancelError in Node.js with node-fetch
export { isCancelError } from './xhrTransferHandler';
export { toBase64, utf8Encode } from './index.native';

// Make sure package.json is included in the TypeScript build output.
Expand Down
19 changes: 5 additions & 14 deletions packages/storage/src/AwsClients/S3/runtime/xhrTransferHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,10 @@ import {
NETWORK_ERROR_MESSAGE,
} from './constants';
import { TransferProgressEvent } from '../../../types/common';
import { CanceledError } from '../../../errors/CanceledError';

const logger = new Logger('xhr-http-handler');

/**
* Internal type for CanceledError thrown by handler when AbortController is called
* with out overwriting.
*/
interface CanceledError extends Error {
__CANCEL__: true;
}

/**
* @internal
*/
Expand Down Expand Up @@ -165,10 +158,10 @@ export const xhrTransferHandler: TransferHandler<
if (!xhr) {
return;
}
const canceledError = Object.assign(
buildHandlerError(CANCELED_ERROR_MESSAGE, CANCELED_ERROR_CODE),
{ __CANCEL__: true }
);
const canceledError = new CanceledError({
name: CANCELED_ERROR_CODE,
message: CANCELED_ERROR_MESSAGE,
});
reject(canceledError);
xhr.abort();
xhr = null;
Expand Down Expand Up @@ -203,8 +196,6 @@ const buildHandlerError = (message: string, name: string): Error => {
return error;
};

export const isCancelError = (error: unknown): boolean =>
!!error && (error as CanceledError).__CANCEL__ === true;
/**
* Convert xhr.getAllResponseHeaders() string to a Record<string, string>. Note that modern browser already returns
* header names in lowercase.
Expand Down
1 change: 0 additions & 1 deletion packages/storage/src/AwsClients/S3/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ export {
SEND_UPLOAD_PROGRESS_EVENT,
s3TransferHandler,
CANCELED_ERROR_MESSAGE,
isCancelError,
CONTENT_SHA256_HEADER,
toBase64,
utf8Encode,
Expand Down
28 changes: 28 additions & 0 deletions packages/storage/src/errors/CanceledError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

import { ErrorParams } from '@aws-amplify/core/internals/utils';
import { StorageError } from './StorageError';

/**
* Internal-only class for CanceledError thrown by XHR handler or multipart upload when cancellation is invoked
* without overwriting behavior.
*
* @internal
*/
export class CanceledError extends StorageError {
constructor(params: ErrorParams) {
super(params);

// TODO: Delete the following 2 lines after we change the build target to >= es2015
this.constructor = CanceledError;
Object.setPrototypeOf(this, CanceledError.prototype);
}
}

/**
* Check if an error is caused by user calling `cancel()` on a upload/download task. If an overwriting error is
* supplied to `task.cancel(errorOverwrite)`, this function will return `false`.
*/
export const isCancelError = (error: unknown): boolean =>
!!error && error instanceof CanceledError;
3 changes: 2 additions & 1 deletion packages/storage/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export {
getProperties,
copy,
getUrl,
isCancelError,
} from './providers/s3';
export * from './types';
// TODO[AllanZhengYP]: support isCancelError in Node.js with node-fetch
export { isCancelError } from './errors/CanceledError';
Original file line number Diff line number Diff line change
Expand Up @@ -47,16 +47,21 @@ export const findCachedUploadParts = async ({

await kvStorage.setItem(UPLOADS_STORAGE_KEY, JSON.stringify(cachedUploads));

const { Parts = [] } = await listParts(s3Config, {
Bucket: bucket,
Key: finalKey,
UploadId: cachedUpload.uploadId,
});

return {
parts: Parts,
uploadId: cachedUpload.uploadId,
};
try {
const { Parts = [] } = await listParts(s3Config, {
Bucket: bucket,
Key: finalKey,
UploadId: cachedUpload.uploadId,
});
return {
parts: Parts,
uploadId: cachedUpload.uploadId,
};
} catch (e) {
// TODO: debug message: failed to list parts. The cached upload will be removed.
await removeCachedUpload(cacheKey);
return null;
}
};

type FileMetadata = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { getUploadsCacheKey, removeCachedUpload } from './uploadCache';
import { uploadPartExecutor } from './uploadPartExecutor';
import { StorageError } from '../../../../../errors/StorageError';
import { Amplify, StorageAccessLevel } from '@aws-amplify/core';
import { CanceledError } from '../../../../../errors/CanceledError';

/**
* Create closure hiding the multipart upload implementation details and expose the upload job and control functions(
Expand Down Expand Up @@ -50,6 +51,7 @@ export const getMultipartUploadHandlers = (
let abortController: AbortController | undefined;
let bucket: string | undefined;
let keyPrefix: string | undefined;
let uploadCacheKey: string | undefined;
// Special flag that differentiates HTTP requests abort error caused by pause() from ones caused by cancel().
// The former one should NOT cause the upload job to throw, but cancels any pending HTTP requests.
// This should be replaced by a special abort reason. However,the support of this API is lagged behind.
Expand Down Expand Up @@ -96,7 +98,7 @@ export const getMultipartUploadHandlers = (

// TODO[AllanZhengYP]: support excludeSubPaths option to exclude sub paths
const finalKey = keyPrefix + key;
const uploadCacheKey = size
uploadCacheKey = size
? getUploadsCacheKey({
file: data instanceof File ? data : undefined,
accessLevel: resolveAccessLevel(uploadDataOptions?.accessLevel),
Expand All @@ -107,27 +109,6 @@ export const getMultipartUploadHandlers = (
})
: undefined;

const abortListener = async () => {
try {
if (isAbortSignalFromPause) {
return;
}
await abortMultipartUpload(s3Config!, {
Bucket: bucket,
Key: finalKey,
UploadId: inProgressUpload?.uploadId,
});
if (uploadCacheKey) {
removeCachedUpload(uploadCacheKey);
}
} catch (e) {
// TODO: debug message: Error cancelling upload task.
} finally {
abortController?.signal.removeEventListener('abort', abortListener);
}
};
abortController?.signal.addEventListener('abort', abortListener);

const dataChunker = getDataChunker(data, size);
const completedPartNumberSet = new Set<number>(
inProgressUpload.completedParts.map(({ PartNumber }) => PartNumber!)
Expand Down Expand Up @@ -214,7 +195,7 @@ export const getMultipartUploadHandlers = (
if (abortSignal?.aborted && isAbortSignalFromPause) {
// TODO: debug message: upload paused
} else {
// TODO: debug message: upload canceled
// Uncaught errors should be exposed to the users.
rejectCallback!(error);
}
});
Expand All @@ -233,7 +214,34 @@ export const getMultipartUploadHandlers = (
startUploadWithResumability();
};
const onCancel = (abortErrorOverwrite?: Error) => {
// 1. abort in-flight API requests
abortController?.abort(abortErrorOverwrite);

const cancelUpload = async () => {
// 2. clear upload cache.
if (uploadCacheKey) {
await removeCachedUpload(uploadCacheKey);
}
// 3. clear multipart upload on server side.
await abortMultipartUpload(s3Config!, {
Bucket: bucket,
Key: keyPrefix! + key,
UploadId: inProgressUpload?.uploadId,
});
};
cancelUpload().catch(e => {
// TODO: debug message: Error cancelling upload task.
});

rejectCallback!(
abortErrorOverwrite ??
// Internal error that should not be exposed to the users. They should use isCancelError() to check if
// the error is caused by cancel().
new CanceledError({
name: 'StorageCanceledError',
message: 'Upload is canceled by user',
})
);
};
return {
multipartUploadJob,
Expand Down
1 change: 0 additions & 1 deletion packages/storage/src/providers/s3/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,3 @@ export {
copy,
getUrl,
} from './apis';
export { isCancelError } from '../../AwsClients/S3/runtime';
2 changes: 1 addition & 1 deletion packages/storage/src/providers/s3/utils/transferTask.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

import { isCancelError } from '../../../AwsClients/S3/runtime';
import { isCancelError } from '../../../errors/CanceledError';
import {
DownloadTask,
TransferTaskState,
Expand Down

0 comments on commit a6ccae1

Please sign in to comment.