Skip to content

Commit

Permalink
Merge pull request #416 from saplingjs/feature/wildcard-filetypes
Browse files Browse the repository at this point in the history
Support wildcards for filetype
  • Loading branch information
groenroos authored Feb 21, 2022
2 parents 51f461d + 60c83b5 commit 1cb6ee1
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 3 deletions.
24 changes: 24 additions & 0 deletions lib/Utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,4 +116,28 @@ export default class Utils {
return false;
}
}


/**
* Test a string against a rule that has asterisk wildcards
*
* @param {string} string String to be tested
* @param {string} rule Pattern to be tested against
* @returns {boolean} Whether or not the string matches the pattern
*/
matchWildcard(string, rule) {
const escapeRegex = string => string.replace(/([.*+?^=!:${}()|[\]/\\])/g, '\\$1');
return new RegExp(`^${String(rule).split('*').map(element => escapeRegex(element)).join('.*')}$`).test(String(string));
}


/**
* Make sure the value passed is an array
*
* @param {any} array Value to be coerced
* @returns {array} Array
*/
coerceArray(array) {
return Array.isArray(array) ? array : [array];
}
}
16 changes: 13 additions & 3 deletions lib/Validation.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
* Make sure the given data is valid.
*/

/* Dependencies */
import Utils from './Utils.js';


/**
* The Validation class
*/
Expand Down Expand Up @@ -366,10 +370,16 @@ export default class Validation {
validateFileType(file, key, rule) {
if (rule.filetype) {
/* Make sure it's an array */
const acceptedTypes = Array.isArray(rule.filetype) ? rule.filetype : [rule.filetype];
const acceptedTypes = new Utils().coerceArray(rule.filetype);

/* The filetype indicators we want to compare against (mime, group or ext) */
const filetypes = [file.mimetype, file.extension, file.group];

/* Ensure the file matches the given filetype */
const match = acceptedTypes.some(accepted => filetypes.some(compared => new Utils().matchWildcard(compared, accepted)));

/* Ensure the file matches the given filetype (mime, group or ext) */
if (!(acceptedTypes.includes(file.mimetype) || acceptedTypes.includes(file.extension) || acceptedTypes.includes(file.group))) {
/* If not, send error */
if (!match) {
this.errors.push({
status: '422',
code: '2001',
Expand Down
25 changes: 25 additions & 0 deletions test/lib/Utils.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,31 @@ test('converts values to true boolean', t => {
t.falsy(t.context.utils.trueBoolean(null));
});

test('matches string against wildcard pattern', t => {
t.true(t.context.utils.matchWildcard('string', 'str*'));
t.true(t.context.utils.matchWildcard('string', '*ing'));
t.true(t.context.utils.matchWildcard('string', '*tr*'));

t.false(t.context.utils.matchWildcard('value', 'str*'));
t.false(t.context.utils.matchWildcard('value', '*ing'));
t.false(t.context.utils.matchWildcard('value', '*tr*'));

t.true(t.context.utils.matchWildcard(5000, '5*'));
t.false(t.context.utils.matchWildcard(6000, '5*'));

/* Even when the pattern has some other regex-y characters */
t.true(t.context.utils.matchWildcard('et tu, brute?', 'et tu, *?'));
t.true(t.context.utils.matchWildcard('file.jpg', 'file.*'));
t.false(t.context.utils.matchWildcard('files', 'file.*'));
});

test('coerces any value into an array', t => {
t.deepEqual(t.context.utils.coerceArray('string'), ['string']);
t.deepEqual(t.context.utils.coerceArray(1), [1]);
t.deepEqual(t.context.utils.coerceArray(false), [false]);
t.deepEqual(t.context.utils.coerceArray([1, 2, 3]), [1, 2, 3]);
});


test.after.always(t => {
fs.chmodSync(path.join(__dirname, '../_data/inaccessible'), 0o755);
Expand Down
28 changes: 28 additions & 0 deletions test/lib/Validation.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,34 @@ test('invalidates invalid file against file type', t => {
t.is(t.context.validator.errors.length, 6);
});

test('validates valid file against file type with wildcards', t => {
t.context.validator.validateFileType({ mimetype: 'image/jpeg' }, 'attachment', { filetype: 'image/*' });
t.is(t.context.validator.errors.length, 0);

t.context.validator.validateFileType({ mimetype: 'video/ogg' }, 'attachment', { filetype: '*/ogg' });
t.is(t.context.validator.errors.length, 0);

t.context.validator.validateFileType({ extension: 'jpg' }, 'attachment', { filetype: '*p*' });
t.is(t.context.validator.errors.length, 0);

t.context.validator.validateFileType({ group: 'image' }, 'attachment', { filetype: '*age' });
t.is(t.context.validator.errors.length, 0);
});

test('invalidates invalid file against file type with wildcards', t => {
t.context.validator.validateFileType({ mimetype: 'video/ogg' }, 'attachment', { filetype: 'image/*' });
t.is(t.context.validator.errors.length, 1);

t.context.validator.validateFileType({ mimetype: 'image/jpeg' }, 'attachment', { filetype: '*/ogg' });
t.is(t.context.validator.errors.length, 2);

t.context.validator.validateFileType({ extension: 'gif' }, 'attachment', { filetype: '*p*' });
t.is(t.context.validator.errors.length, 3);

t.context.validator.validateFileType({ group: 'video' }, 'attachment', { filetype: '*age' });
t.is(t.context.validator.errors.length, 4);
});


/* validateFileMinwidth */

Expand Down

0 comments on commit 1cb6ee1

Please sign in to comment.