Remove Content-Type header check in Files POST requests (#16176)

* remove request header check in files POST

* basic test

* it's filename_download instead of file_download

* unused vars begone

* make type required when creating file via JSON

* fix blackbox test

Co-authored-by: Rijk van Zanten <rijkvanzanten@me.com>
This commit is contained in:
Azri Kahar
2022-11-02 00:06:09 +08:00
committed by GitHub
parent 0b7630504a
commit 17c0ef9182
4 changed files with 120 additions and 7 deletions

View File

@@ -5,7 +5,7 @@ import express, { RequestHandler } from 'express';
import Joi from 'joi';
import path from 'path';
import env from '../env';
import { ForbiddenException, InvalidPayloadException, UnsupportedMediaTypeException } from '../exceptions';
import { ForbiddenException, InvalidPayloadException } from '../exceptions';
import { respond } from '../middleware/respond';
import useCollection from '../middleware/use-collection';
import { validateBatch } from '../middleware/validate-batch';
@@ -121,10 +121,6 @@ router.post(
'/',
asyncHandler(multipartHandler),
asyncHandler(async (req, res, next) => {
if (req.is('multipart/form-data') === false) {
throw new UnsupportedMediaTypeException(`Unsupported Content-Type header`);
}
const service = new FilesService({
accountability: req.accountability,
schema: req.schema,

View File

@@ -1,7 +1,8 @@
import exifr from 'exifr';
import knex, { Knex } from 'knex';
import { MockClient, Tracker, getTracker } from 'knex-mock-client';
import { FilesService } from '.';
import { FilesService, ItemsService } from '.';
import { InvalidPayloadException } from '../exceptions';
jest.mock('exifr');
jest.mock('../../src/database/index', () => {
@@ -20,9 +21,49 @@ describe('Integration Tests', () => {
afterEach(() => {
tracker.reset();
jest.clearAllMocks();
});
describe('Services / Files', () => {
describe('createOne', () => {
let service: FilesService;
let superCreateOne: jest.SpyInstance;
beforeEach(() => {
service = new FilesService({
knex: db,
schema: { collections: {}, relations: [] },
});
superCreateOne = jest.spyOn(ItemsService.prototype, 'createOne').mockImplementation(jest.fn());
});
it('throws InvalidPayloadException when "type" is not provided', async () => {
try {
await service.createOne({
title: 'Test File',
storage: 'local',
filename_download: 'test_file',
});
} catch (err: any) {
expect(err).toBeInstanceOf(InvalidPayloadException);
expect(err.message).toBe('"type" is required');
}
expect(superCreateOne).not.toHaveBeenCalled();
});
it('creates a file entry when "type" is provided', async () => {
await service.createOne({
title: 'Test File',
storage: 'local',
filename_download: 'test_file',
type: 'application/octet-stream',
});
expect(superCreateOne).toHaveBeenCalled();
});
});
describe('getMetadata', () => {
let service: FilesService;
let exifrParseSpy: jest.SpyInstance<any>;

View File

@@ -10,7 +10,7 @@ import { promisify } from 'util';
import { lookup } from 'dns';
import emitter from '../emitter';
import env from '../env';
import { ForbiddenException, ServiceUnavailableException } from '../exceptions';
import { ForbiddenException, InvalidPayloadException, ServiceUnavailableException } from '../exceptions';
import logger from '../logger';
import storage from '../storage';
import { AbstractServiceOptions, File, PrimaryKey, MutationOptions, Metadata } from '../types';
@@ -268,6 +268,19 @@ export class FilesService extends ItemsService {
return await this.uploadOne(fileResponse.data, payload);
}
/**
* Create a file (only applicable when it is not a multipart/data POST request)
* Useful for associating metadata with existing file in storage
*/
async createOne(data: Partial<File>, opts?: MutationOptions): Promise<PrimaryKey> {
if (!data.type) {
throw new InvalidPayloadException(`"type" is required`);
}
const key = await super.createOne(data, opts);
return key;
}
/**
* Delete a file
*/