Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions lib/api/apiUtils/bucket/parseLikeExpression.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
* parse LIKE expressions
* @param {string} regex - regex pattern
* @return {object} MongoDB search object
*/
function parseLikeExpression(regex) {
if (typeof regex !== 'string') {
return null;
}
const split = regex.split('/');
if (split.length < 3 || split[0] !== '') {
return { $regex: regex };
}
const pattern = split.slice(1, split.length - 1).join('/');
const regexOpt = split[split.length - 1];
return { $regex: pattern, $options: regexOpt };
}

module.exports = parseLikeExpression;
13 changes: 10 additions & 3 deletions lib/api/apiUtils/bucket/parseWhere.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
const parseLikeExpression = require('./parseLikeExpression');

/*
This code is based on code from https://github.com/olehch/sqltomongo
Expand Down Expand Up @@ -53,7 +54,7 @@ function parseWhere(root) {
const e2 = parseWhere(root[operator][1]);

// eslint-disable-next-line
return { '$and' : [
return { '$and' : [
e1,
e2,
] };
Expand All @@ -62,15 +63,21 @@ function parseWhere(root) {
const e2 = parseWhere(root[operator][1]);

// eslint-disable-next-line
return { '$or' : [
return { '$or' : [
e1,
e2,
] };
}
const field = root[operator][0];
const value = root[operator][1];
const expr = exprMapper[operator];
const obj = {};
obj[`value.${field}`] = { [expr]: root[operator][1] };

if (operator === 'LIKE') {
obj[`value.${field}`] = parseLikeExpression(value);
} else {
obj[`value.${field}`] = { [expr]: value };
}

return obj;
}
Expand Down
50 changes: 50 additions & 0 deletions tests/functional/aws-node-sdk/test/mdSearch/basicSearch.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,25 @@ runIfMongo('Basic search', () => {
encodedSearch, objectKey, done);
});

it('should list object with regex searched for system metadata', done => {
const encodedSearch = encodeURIComponent('key LIKE "find.*"');
return runAndCheckSearch(s3Client, bucketName,
encodedSearch, objectKey, done);
});

it('should list object with regex searched for system metadata with flags',
done => {
const encodedSearch = encodeURIComponent('key LIKE "/FIND.*/i"');
return runAndCheckSearch(s3Client, bucketName,
encodedSearch, objectKey, done);
});

it('should return empty when no object match regex', done => {
const encodedSearch = encodeURIComponent('key LIKE "/NOTFOUND.*/i"');
return runAndCheckSearch(s3Client, bucketName,
encodedSearch, null, done);
});

it('should list object with searched for user metadata', done => {
const encodedSearch =
encodeURIComponent(`x-amz-meta-food="${userMetadata.food}"`);
Expand Down Expand Up @@ -99,3 +118,34 @@ runIfMongo('Search when no objects in bucket', () => {
encodedSearch, null, done);
});
});

runIfMongo('Invalid regular expression searches', () => {
const bucketName = `noobjectbucket${Date.now()}`;
before(done => {
s3Client.createBucket({ Bucket: bucketName }, done);
});

after(done => {
s3Client.deleteBucket({ Bucket: bucketName }, done);
});

it('should return error if pattern is invalid', done => {
const encodedSearch = encodeURIComponent('key LIKE "/((helloworld/"');
const testError = {
code: 'InternalError',
message: 'We encountered an internal error. Please try again.',
};
return runAndCheckSearch(s3Client, bucketName,
encodedSearch, testError, done);
});

it('should return error if regex flag is invalid', done => {
const encodedSearch = encodeURIComponent('key LIKE "/((helloworld/ii"');
const testError = {
code: 'InternalError',
message: 'We encountered an internal error. Please try again.',
};
return runAndCheckSearch(s3Client, bucketName,
encodedSearch, testError, done);
});
});
14 changes: 10 additions & 4 deletions tests/functional/aws-node-sdk/test/mdSearch/utils/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,23 +20,29 @@ testUtils.runIfMongo = process.env.S3METADATA === 'mongodb' ?
describe : describe.skip;

testUtils.runAndCheckSearch = (s3Client, bucketName, encodedSearch,
keyToFind, done) => {
testResult, done) => {
const searchRequest = s3Client.listObjects({ Bucket: bucketName });
searchRequest.on('build', () => {
searchRequest.httpRequest.path =
`${searchRequest.httpRequest.path}?search=${encodedSearch}`;
});
searchRequest.on('success', res => {
if (keyToFind) {
if (testResult) {
assert(res.data.Contents[0], 'should be Contents listed');
assert.strictEqual(res.data.Contents[0].Key, keyToFind);
assert.strictEqual(res.data.Contents[0].Key, testResult);
assert.strictEqual(res.data.Contents.length, 1);
} else {
assert.strictEqual(res.data.Contents.length, 0);
}
return done();
});
searchRequest.on('error', done);
searchRequest.on('error', err => {
if (testResult) {
assert.strictEqual(err.code, testResult.code);
assert.strictEqual(err.message, testResult.message);
}
return done();
});
searchRequest.send();
};

Expand Down
53 changes: 53 additions & 0 deletions tests/unit/api/parseLikeExpression.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
const assert = require('assert');
const parseLikeExpression =
require('../../../lib/api/apiUtils/bucket/parseLikeExpression');

describe('parseLikeExpression', () => {
const tests = [
{
input: '',
output: { $regex: '' },
},
{
input: 'ice-cream-cone',
output: { $regex: 'ice-cream-cone' },
},
{
input: '/ice-cream-cone/',
output: { $regex: 'ice-cream-cone', $options: '' },
},
{
input: '/ice-cream-cone/i',
output: { $regex: 'ice-cream-cone', $options: 'i' },
},
{
input: 'an/ice-cream-cone/',
output: { $regex: 'an/ice-cream-cone/' },
},
{
input: '///',
output: { $regex: '/', $options: '' },
},
];
tests.forEach(test => it('should return correct MongoDB query object: ' +
`"${test.input}" => ${JSON.stringify(test.output)}`, () => {
const res = parseLikeExpression(test.input);
assert.deepStrictEqual(res, test.output);
}));
const badInputTests = [
{
input: null,
output: null,
},
{
input: 1235,
output: null,
},
];
badInputTests.forEach(test => it(
'should return null if input is not a string ' +
`"${test.input}" => ${JSON.stringify(test.output)}`, () => {
const res = parseLikeExpression(test.input);
assert.deepStrictEqual(res, test.output);
}));
});