Skip to content

Commit

Permalink
Merge pull request #8 from dabapps/feature-allow-acl-as-a-passthrough
Browse files Browse the repository at this point in the history
Feature allow acl as a passthrough
  • Loading branch information
JakeSidSmith authored Aug 22, 2019
2 parents ea2d5b5 + cf1ea69 commit 802d9a8
Show file tree
Hide file tree
Showing 6 changed files with 85 additions and 11 deletions.
5 changes: 3 additions & 2 deletions src/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@ import {
ActionSet,
BeginAction,
FailureAction,
FileAndACL,
FileUploadOptions,
SuccessAction,
} from './types';
import { uploadFileToS3 } from './upload-file-to-s3';

export const uploadFileWithLoading = <S>(
actionSet: ActionSet,
file: File,
file: File | FileAndACL,
dispatch: ThunkDispatch<S, unknown, AnyAction>
) => {
dispatch({ type: actionSet.REQUEST });
Expand All @@ -30,7 +31,7 @@ export const uploadFileWithLoading = <S>(
export const createFileUploadAction = <S>(
actionSet: ActionSet,
options?: FileUploadOptions
) => (files: ReadonlyArray<File>) => (
) => (files: ReadonlyArray<File | FileAndACL>) => (
dispatch: ThunkDispatch<S, unknown, AnyAction>
) => {
dispatch<BeginAction>({
Expand Down
5 changes: 5 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ export interface UploadState {
error: undefined | ReadonlyArray<unknown>;
}

export interface FileAndACL {
file: File;
acl: 'private' | 'public-read' | 'public-read-write' | 'authenticated-read';
}

export interface BeginAction extends Action {
payload: number;
}
Expand Down
29 changes: 22 additions & 7 deletions src/upload-file-to-s3.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import axios, { AxiosResponse } from 'axios';
import * as Cookies from 'js-cookie';
import { POST } from './constants';
import { UploadData, UploadFormFieldsAndFile } from './types';
import { FileAndACL, UploadData, UploadFormFieldsAndFile } from './types';

export const configureFormData = (data: UploadFormFieldsAndFile) => {
const formData = new FormData();
Expand Down Expand Up @@ -51,23 +51,38 @@ export const uploadFileToSignedUrl = (
});
};

export const getUploadForm = (file: File): Promise<UploadData> => {
const isFile = (file: File | FileAndACL): file is File =>
file && !('acl' in file);

export const getUploadForm = (file: File | FileAndACL): Promise<UploadData> => {
const data = isFile(file)
? {
filename: file.name,
}
: {
acl: file.acl,
filename: file.file.name,
};

return axios
.request({
method: POST,
url: '/api/s3-file-uploads/',
headers: {
'X-CSRFToken': Cookies.get('csrftoken'),
},
data: {
filename: file.name,
},
data,
})
.then((uploadResponse: AxiosResponse<UploadData>) => {
return uploadFileToSignedUrl(uploadResponse.data, file);
return uploadFileToSignedUrl(
uploadResponse.data,
isFile(file) ? file : file.file
);
});
};

export const uploadFileToS3 = (file: File): Promise<UploadData> => {
export const uploadFileToS3 = (
file: File | FileAndACL
): Promise<UploadData> => {
return getUploadForm(file);
};
3 changes: 2 additions & 1 deletion tests/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { MockPromise } from './helpers/mock-promise';
import {
createActionSet,
createFileUploadAction,
FileAndACL,
FileUploadOptions,
} from '../src';
import { uploadFileWithLoading } from '../src/actions';
Expand All @@ -14,7 +15,7 @@ describe('upload actions', () => {
const mockUploadFileToS3 = jest
.spyOn(requests, 'uploadFileToS3')
.mockImplementation(
(file: File) =>
(file: File | FileAndACL) =>
new MockPromise({ arguments: [file], thenCalls: [], catchCalls: [] })
);

Expand Down
6 changes: 6 additions & 0 deletions tests/helpers/stubs.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { UploadData, UploadForm, UploadFormFields } from '../../src/types';
import { FileAndACL } from '../../src/types';

export const mockedUploadFormFields: UploadFormFields = {
AWSAccessKeyId: 'aaaaaa',
Expand All @@ -25,3 +26,8 @@ export const mockedUploadData: UploadData = {
};

export const mockedFile = new File([], 'llama');

export const mockedFileAndACL: FileAndACL = {
file: new File([], 'drama'),
acl: 'public-read',
};
48 changes: 47 additions & 1 deletion tests/update-file-to-s3.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@ jest.mock('axios', () => ({ default: mockAxios }));

import { uploadFileToS3 } from '../src';
import * as requests from '../src/upload-file-to-s3';
import { mockedFile, mockedUploadData } from './helpers/stubs';
import {
mockedFile,
mockedFileAndACL,
mockedUploadData,
} from './helpers/stubs';

describe('Django S3 File Upload', () => {
beforeEach(() => {
Expand Down Expand Up @@ -59,6 +63,48 @@ describe('Django S3 File Upload', () => {
mockedUploadData,
mockedFile
);

spyOnUploadFileToSignedUrl.mockRestore();
});
it('should make an axios request and then call uploadFileToSignedUrl with ACL data included', () => {
requests.getUploadForm(mockedFileAndACL);
// Get the request calls
const { requestCalls } = mockAxios;

// Check that it was called
expect(requestCalls.length).toBe(1);

expect(requestCalls[0].arguments).toEqual([
{
method: 'POST',
url: '/api/s3-file-uploads/',
headers: {
'X-CSRFToken': undefined,
},
data: {
filename: 'drama',
acl: 'public-read',
},
},
]);

// Get the .then calls
const { thenCalls } = requestCalls[0];

const spyOnUploadFileToSignedUrl = jest.spyOn(
requests,
'uploadFileToSignedUrl'
);

// Manually trigger .then
thenCalls[0].arguments[0]({ data: mockedUploadData });
expect(spyOnUploadFileToSignedUrl).toHaveBeenCalledTimes(1);
expect(spyOnUploadFileToSignedUrl).toHaveBeenCalledWith(
mockedUploadData,
mockedFileAndACL.file
);

spyOnUploadFileToSignedUrl.mockRestore();
});
});

Expand Down

0 comments on commit 802d9a8

Please sign in to comment.