Skip to content

Commit

Permalink
Merge branch 'next/release' into chore/creds
Browse files Browse the repository at this point in the history
  • Loading branch information
elorzafe authored Sep 13, 2023
2 parents 29c3a49 + d65f71f commit 8abdcb5
Show file tree
Hide file tree
Showing 7 changed files with 492 additions and 358 deletions.
196 changes: 110 additions & 86 deletions packages/storage/__tests__/providers/s3/apis/copy.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ import { Credentials } from '@aws-sdk/types';
import { Amplify, StorageAccessLevel } from '@aws-amplify/core';
import { copyObject } from '../../../../src/providers/s3/utils/client';
import { copy } from '../../../../src/providers/s3/apis';
import {
StorageCopySourceOptions,
StorageCopyDestinationOptions,
} from '../../../../src/types';

jest.mock('../../../../src/providers/s3/utils/client');
jest.mock('@aws-amplify/core', () => ({
Expand All @@ -24,6 +28,7 @@ const destinationKey = 'destinationKey';
const bucket = 'bucket';
const region = 'region';
const targetIdentityId = 'targetIdentityId';
const defaultIdentityId = 'defaultIdentityId';
const copyResult = { key: destinationKey };
const credentials: Credentials = {
accessKeyId: 'accessKeyId',
Expand All @@ -39,72 +44,17 @@ const copyObjectClientBaseParams = {
MetadataDirective: 'COPY',
};

/**
* bucket is appended at start if it's a sourceKey
* guest: public/${key}`
* private: private/${targetIdentityId}/${key}`
* protected: protected/${targetIdentityId}/${key}`
*/
const buildClientRequestKey = (
key: string,
KeyType: 'source' | 'destination',
accessLevel: StorageAccessLevel
) => {
const targetIdentityId = 'targetIdentityId';
const bucket = 'bucket';
const finalAccessLevel = accessLevel == 'guest' ? 'public' : accessLevel;
let finalKey = KeyType == 'source' ? `${bucket}/` : '';
finalKey += `${finalAccessLevel}/`;
finalKey += finalAccessLevel != 'public' ? `${targetIdentityId}/` : '';
finalKey += `${key}`;
return finalKey;
};

const interAccessLevelTest = async (
sourceAccessLevel,
destinationAccessLevel
) => {
expect.assertions(3);
const source = {
key: sourceKey,
accessLevel: sourceAccessLevel,
};
sourceAccessLevel == 'protected'
? (source['targetIdentityId'] = targetIdentityId)
: null;

expect(
await copy({
source,
destination: {
key: destinationKey,
accessLevel: destinationAccessLevel,
},
})
).toEqual(copyResult);
expect(copyObject).toBeCalledTimes(1);
expect(copyObject).toHaveBeenCalledWith(copyObjectClientConfig, {
...copyObjectClientBaseParams,
CopySource: buildClientRequestKey(sourceKey, 'source', sourceAccessLevel),
Key: buildClientRequestKey(
destinationKey,
'destination',
destinationAccessLevel
),
});
};

describe('copy API', () => {
beforeAll(() => {
mockFetchAuthSession.mockResolvedValue({
credentials,
identityId: targetIdentityId,
identityId: defaultIdentityId,
});
mockGetConfig.mockReturnValue({
Storage: {
S3: {
bucket: 'bucket',
region: 'region',
bucket,
region,
},
},
});
Expand All @@ -120,40 +70,114 @@ describe('copy API', () => {
afterEach(() => {
jest.clearAllMocks();
});

describe('Copy from guest to all access levels', () => {
it('Should copy guest -> guest', async () =>
await interAccessLevelTest('guest', 'guest'));
it('Should copy guest -> private', async () =>
await interAccessLevelTest('guest', 'private'));
it('Should copy guest -> protected', async () =>
await interAccessLevelTest('guest', 'protected'));
});

describe('Copy from private to all access levels', () => {
it('Should copy private -> guest', async () =>
await interAccessLevelTest('private', 'guest'));
it('Should copy private -> private', async () =>
await interAccessLevelTest('private', 'private'));
it('Should copy private -> protected', async () =>
await interAccessLevelTest('private', 'protected'));
});

describe('Copy from protected to all access levels', () => {
it('Should copy protected -> guest', async () =>
await interAccessLevelTest('protected', 'guest'));
it('Should copy protected -> private', async () =>
await interAccessLevelTest('protected', 'private'));
it('Should copy protected -> protected', async () =>
await interAccessLevelTest('protected', 'protected'));
});
[
{
source: { accessLevel: 'guest' },
destination: { accessLevel: 'guest' },
expectedSourceKey: `${bucket}/public/${sourceKey}`,
expectedDestinationKey: `public/${destinationKey}`,
},
{
source: { accessLevel: 'guest' },
destination: { accessLevel: 'private' },
expectedSourceKey: `${bucket}/public/${sourceKey}`,
expectedDestinationKey: `private/${defaultIdentityId}/${destinationKey}`,
},
{
source: { accessLevel: 'guest' },
destination: { accessLevel: 'protected' },
expectedSourceKey: `${bucket}/public/${sourceKey}`,
expectedDestinationKey: `protected/${defaultIdentityId}/${destinationKey}`,
},
{
source: { accessLevel: 'private' },
destination: { accessLevel: 'guest' },
expectedSourceKey: `${bucket}/private/${defaultIdentityId}/${sourceKey}`,
expectedDestinationKey: `public/${destinationKey}`,
},
{
source: { accessLevel: 'private' },
destination: { accessLevel: 'private' },
expectedSourceKey: `${bucket}/private/${defaultIdentityId}/${sourceKey}`,
expectedDestinationKey: `private/${defaultIdentityId}/${destinationKey}`,
},
{
source: { accessLevel: 'private' },
destination: { accessLevel: 'protected' },
expectedSourceKey: `${bucket}/private/${defaultIdentityId}/${sourceKey}`,
expectedDestinationKey: `protected/${defaultIdentityId}/${destinationKey}`,
},
{
source: { accessLevel: 'protected' },
destination: { accessLevel: 'guest' },
expectedSourceKey: `${bucket}/protected/${defaultIdentityId}/${sourceKey}`,
expectedDestinationKey: `public/${destinationKey}`,
},
{
source: { accessLevel: 'protected' },
destination: { accessLevel: 'private' },
expectedSourceKey: `${bucket}/protected/${defaultIdentityId}/${sourceKey}`,
expectedDestinationKey: `private/${defaultIdentityId}/${destinationKey}`,
},
{
source: { accessLevel: 'protected' },
destination: { accessLevel: 'protected' },
expectedSourceKey: `${bucket}/protected/${defaultIdentityId}/${sourceKey}`,
expectedDestinationKey: `protected/${defaultIdentityId}/${destinationKey}`,
},
{
source: { accessLevel: 'protected', targetIdentityId },
destination: { accessLevel: 'guest' },
expectedSourceKey: `${bucket}/protected/${targetIdentityId}/${sourceKey}`,
expectedDestinationKey: `public/${destinationKey}`,
},
{
source: { accessLevel: 'protected', targetIdentityId },
destination: { accessLevel: 'private' },
expectedSourceKey: `${bucket}/protected/${targetIdentityId}/${sourceKey}`,
expectedDestinationKey: `private/${defaultIdentityId}/${destinationKey}`,
},
{
source: { accessLevel: 'protected', targetIdentityId },
destination: { accessLevel: 'protected' },
expectedSourceKey: `${bucket}/protected/${targetIdentityId}/${sourceKey}`,
expectedDestinationKey: `protected/${defaultIdentityId}/${destinationKey}`,
},
].forEach(
({ source, destination, expectedSourceKey, expectedDestinationKey }) => {
const targetIdentityIdMsg = source?.targetIdentityId
? `with targetIdentityId`
: '';
it(`should copy ${source.accessLevel} ${targetIdentityIdMsg} -> ${destination.accessLevel}`, async () => {
expect.assertions(3);
expect(
await copy({
source: {
...(source as StorageCopySourceOptions),
key: sourceKey,
},
destination: {
...(destination as StorageCopyDestinationOptions),
key: destinationKey,
},
})
).toEqual(copyResult);
expect(copyObject).toBeCalledTimes(1);
expect(copyObject).toHaveBeenCalledWith(copyObjectClientConfig, {
...copyObjectClientBaseParams,
CopySource: expectedSourceKey,
Key: expectedDestinationKey,
});
});
}
);
});

describe('Error Path Cases:', () => {
afterEach(() => {
jest.clearAllMocks();
});
it('Should return a not found error', async () => {
it('should return a not found error', async () => {
mockCopyObject.mockRejectedValueOnce(
Object.assign(new Error(), {
$metadata: { httpStatusCode: 404 },
Expand Down
99 changes: 63 additions & 36 deletions packages/storage/__tests__/providers/s3/apis/downloadData.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Amplify } from '@aws-amplify/core';
import { getObject } from '../../../../src/providers/s3/utils/client';
import { downloadData } from '../../../../src/providers/s3';
import { createDownloadTask } from '../../../../src/providers/s3/utils';
import { StorageOptions } from '../../../../src/types';

jest.mock('../../../../src/providers/s3/utils/client');
jest.mock('../../../../src/providers/s3/utils');
Expand All @@ -16,14 +17,17 @@ jest.mock('@aws-amplify/core', () => ({
fetchAuthSession: jest.fn(),
},
},
fetchAuthSession: jest.fn(),
}));
const credentials: Credentials = {
accessKeyId: 'accessKeyId',
sessionToken: 'sessionToken',
secretAccessKey: 'secretAccessKey',
};
const identityId = 'identityId';
const key = 'key';
const bucket = 'bucket';
const region = 'region';
const targetIdentityId = 'targetIdentityId';
const defaultIdentityId = 'defaultIdentityId';

const mockFetchAuthSession = Amplify.Auth.fetchAuthSession as jest.Mock;
const mockCreateDownloadTask = createDownloadTask as jest.Mock;
Expand All @@ -35,13 +39,13 @@ describe('downloadData', () => {
beforeAll(() => {
mockFetchAuthSession.mockResolvedValue({
credentials,
identityId: identityId,
identityId: defaultIdentityId,
});
mockGetConfig.mockReturnValue({
Storage: {
S3: {
bucket: 'bucket',
region: 'region',
bucket,
region,
},
},
});
Expand All @@ -56,38 +60,61 @@ describe('downloadData', () => {
expect(downloadData({ key: 'key' })).toBe('downloadTask');
});

it('should supply the correct parameters to getObject API handler', async () => {
expect.assertions(2);
(getObject as jest.Mock).mockResolvedValueOnce({ Body: 'body' });
const onProgress = jest.fn();
const targetIdentityId = 'targetIdentityId';
const accessLevel = 'protected';
const key = 'key';
downloadData({
key,
options: {
targetIdentityId,
accessLevel,
useAccelerateEndpoint: true,
onProgress,
},
[
{
expectedKey: `public/${key}`,
},
{
options: { accessLevel: 'guest' },
expectedKey: `public/${key}`,
},
{
options: { accessLevel: 'private' },
expectedKey: `private/${defaultIdentityId}/${key}`,
},
{
options: { accessLevel: 'protected' },
expectedKey: `protected/${defaultIdentityId}/${key}`,
},
{
options: { accessLevel: 'protected', targetIdentityId },
expectedKey: `protected/${targetIdentityId}/${key}`,
},
].forEach(({ options, expectedKey }) => {
const accessLevelMsg = options?.accessLevel ?? 'default';
const targetIdentityIdMsg = options?.targetIdentityId
? `and targetIdentityId`
: '';

it(`should supply the correct parameters to getObject API handler with ${accessLevelMsg} accessLevel ${targetIdentityIdMsg}`, async () => {
expect.assertions(2);
(getObject as jest.Mock).mockResolvedValueOnce({ Body: 'body' });
const onProgress = jest.fn();
downloadData({
key,
options: {
...(options as StorageOptions),
useAccelerateEndpoint: true,
onProgress,
},
});
const job = mockCreateDownloadTask.mock.calls[0][0].job;
await job();
expect(getObject).toBeCalledTimes(1);
expect(getObject).toHaveBeenCalledWith(
{
credentials,
region,
useAccelerateEndpoint: true,
onDownloadProgress: onProgress,
abortSignal: expect.any(AbortSignal),
},
{
Bucket: bucket,
Key: expectedKey,
}
);
});
const job = mockCreateDownloadTask.mock.calls[0][0].job;
await job();
expect(getObject).toBeCalledTimes(1);
expect(getObject).toHaveBeenCalledWith(
{
credentials,
region: 'region',
useAccelerateEndpoint: true,
onDownloadProgress: onProgress,
abortSignal: expect.any(AbortSignal),
},
{
Bucket: 'bucket',
Key: `${accessLevel}/${targetIdentityId}/${key}`,
}
);
});

it('should assign the getObject API handler response to the result', async () => {
Expand Down
Loading

0 comments on commit 8abdcb5

Please sign in to comment.