mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Files] Validate mime type when files are selected (#159503)
## Summary Closes https://github.com/elastic/kibana/issues/155168 ### Checklist Delete any items that are not applicable to this PR. - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios ### For maintainers - [x] This was checked for breaking API changes and was [labeled appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
536a8d4194
commit
2f1c2fa3e7
3 changed files with 60 additions and 15 deletions
|
@ -39,4 +39,10 @@ export const i18nTexts = {
|
|||
'File is too large. Maximum size is {expectedSize, plural, one {# byte} other {# bytes} }.',
|
||||
values: { expectedSize },
|
||||
}),
|
||||
mimeTypeNotSupported: (mimeType: string, supportedMimeTypes: string) =>
|
||||
i18n.translate('sharedUXPackages.fileUpload.mimeTypeNotSupportedErrorMessage', {
|
||||
defaultMessage:
|
||||
'File mime type "{mimeType}" is not supported. Supported mime types are: {supportedMimeTypes}.',
|
||||
values: { mimeType, supportedMimeTypes },
|
||||
}),
|
||||
};
|
||||
|
|
|
@ -33,7 +33,12 @@ describe('UploadState', () => {
|
|||
filesClient.create.mockReturnValue(of({ file: { id: 'test' } as FileJSON }) as any);
|
||||
filesClient.upload.mockReturnValue(of(undefined) as any);
|
||||
uploadState = new UploadState(
|
||||
{ id: 'test', http: {}, maxSizeBytes: 1000 } as FileKindBrowser,
|
||||
{
|
||||
id: 'test',
|
||||
http: {},
|
||||
maxSizeBytes: 1000,
|
||||
allowedMimeTypes: ['text/plain', 'image/png'],
|
||||
} as FileKindBrowser,
|
||||
filesClient,
|
||||
{},
|
||||
imageMetadataFactory
|
||||
|
@ -73,8 +78,8 @@ describe('UploadState', () => {
|
|||
|
||||
it('uploads all provided files', async () => {
|
||||
testScheduler.run(({ expectObservable, cold, flush }) => {
|
||||
const file1 = { name: 'test', size: 1 } as File;
|
||||
const file2 = { name: 'test 2', size: 1 } as File;
|
||||
const file1 = { name: 'test', size: 1, type: 'text/plain' } as File;
|
||||
const file2 = { name: 'test 2', size: 1, type: 'text/plain' } as File;
|
||||
|
||||
uploadState.setFiles([file1, file2]);
|
||||
|
||||
|
@ -120,7 +125,7 @@ describe('UploadState', () => {
|
|||
filesClient.upload.mockReturnValue(of(undefined).pipe(delay(10)) as any);
|
||||
filesClient.delete.mockReturnValue(of(undefined) as any);
|
||||
|
||||
const file1 = { name: 'test' } as File;
|
||||
const file1 = { name: 'test', type: 'text/plain' } as File;
|
||||
const file2 = { name: 'test 2.png', type: 'image/png' } as File;
|
||||
|
||||
uploadState.setFiles([file1, file2]);
|
||||
|
@ -160,7 +165,7 @@ describe('UploadState', () => {
|
|||
expect(filesClient.create).toHaveBeenNthCalledWith(1, {
|
||||
kind: 'test',
|
||||
meta: { myMeta: true },
|
||||
mimeType: undefined,
|
||||
mimeType: 'text/plain',
|
||||
name: 'test',
|
||||
});
|
||||
expect(filesClient.create).toHaveBeenNthCalledWith(2, {
|
||||
|
@ -192,6 +197,28 @@ describe('UploadState', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('throws for files, which mime-type is not supported', () => {
|
||||
testScheduler.run(({ expectObservable }) => {
|
||||
const file = {
|
||||
name: 'script.sh',
|
||||
size: 123,
|
||||
type: 'text/x-sh',
|
||||
} as File;
|
||||
uploadState.setFiles([file]);
|
||||
expectObservable(uploadState.files$).toBe('a', {
|
||||
a: [
|
||||
{
|
||||
file,
|
||||
status: 'idle',
|
||||
error: new Error(
|
||||
'File mime type "text/x-sh" is not supported. Supported mime types are: text/plain, image/png.'
|
||||
),
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('option "allowRepeatedUploads" calls clear after upload is done', () => {
|
||||
testScheduler.run(({ expectObservable, cold }) => {
|
||||
uploadState = new UploadState(
|
||||
|
|
|
@ -97,15 +97,22 @@ export class UploadState {
|
|||
return this.uploading$.getValue();
|
||||
}
|
||||
|
||||
private validateFiles(files: File[]): undefined | string {
|
||||
if (
|
||||
this.fileKind.maxSizeBytes != null &&
|
||||
files.some((file) => file.size > this.fileKind.maxSizeBytes!)
|
||||
) {
|
||||
return i18nTexts.fileTooLarge(String(this.fileKind.maxSizeBytes));
|
||||
private readonly validateFile = (file: File): void => {
|
||||
const fileKind = this.fileKind;
|
||||
|
||||
if (fileKind.maxSizeBytes != null && file.size > this.fileKind.maxSizeBytes!) {
|
||||
const message = i18nTexts.fileTooLarge(String(this.fileKind.maxSizeBytes));
|
||||
throw new Error(message);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (fileKind.allowedMimeTypes != null && !fileKind.allowedMimeTypes.includes(file.type)) {
|
||||
const message = i18nTexts.mimeTypeNotSupported(
|
||||
file.type,
|
||||
fileKind.allowedMimeTypes.join(', ')
|
||||
);
|
||||
throw new Error(message);
|
||||
}
|
||||
};
|
||||
|
||||
public setFiles = (files: File[]): void => {
|
||||
if (this.isUploading()) {
|
||||
|
@ -117,14 +124,19 @@ export class UploadState {
|
|||
this.error$.next(undefined);
|
||||
}
|
||||
|
||||
const validationError = this.validateFiles(files);
|
||||
let error: undefined | Error;
|
||||
try {
|
||||
files.forEach(this.validateFile);
|
||||
} catch (err) {
|
||||
error = err;
|
||||
}
|
||||
|
||||
this.files$$.next(
|
||||
files.map((file) =>
|
||||
createStateSubject<FileState>({
|
||||
file,
|
||||
status: 'idle',
|
||||
error: validationError ? new Error(validationError) : undefined,
|
||||
error,
|
||||
})
|
||||
)
|
||||
);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue