var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray")); var equalObjects = require('./equals').default; var decode = require('./decode').default; var ParseError = require('./ParseError').default; var ParsePolygon = require('./ParsePolygon').default; var ParseGeoPoint = require('./ParseGeoPoint').default; function contains(haystack, needle) { if (needle && needle.__type && (needle.__type === 'Pointer' || needle.__type === 'Object')) { for (var i in haystack) { var ptr = haystack[i]; if (typeof ptr === 'string' && ptr === needle.objectId) { return true; } if (ptr.className === needle.className && ptr.objectId === needle.objectId) { return true; } } return false; } if (Array.isArray(needle)) { for (var need of needle) { if (contains(haystack, need)) { return true; } } } return haystack.indexOf(needle) > -1; } function transformObject(object) { if (object._toFullJSON) { return object._toFullJSON(); } return object; } function matchesQuery(className, object, objects, query) { if (object.className !== className) { return false; } var obj = object; var q = query; if (object.toJSON) { obj = object.toJSON(); } if (query.toJSON) { q = query.toJSON().where; } obj.className = className; for (var field in q) { if (!matchesKeyConstraints(className, obj, objects, field, q[field])) { return false; } } return true; } function equalObjectsGeneric(obj, compareTo, eqlFn) { if (Array.isArray(obj)) { for (var i = 0; i < obj.length; i++) { if (eqlFn(obj[i], compareTo)) { return true; } } return false; } return eqlFn(obj, compareTo); } function relativeTimeToDate(text) { var now = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : new Date(); text = text.toLowerCase(); var parts = text.split(' '); parts = parts.filter(function (part) { return part !== ''; }); var future = parts[0] === 'in'; var past = parts[parts.length - 1] === 'ago'; if (!future && !past && text !== 'now') { return { status: 'error', info: "Time should either start with 'in' or end with 'ago'" }; } if (future && past) { return { status: 'error', info: "Time cannot have both 'in' and 'ago'" }; } if (future) { parts = parts.slice(1); } else { parts = parts.slice(0, parts.length - 1); } if (parts.length % 2 !== 0 && text !== 'now') { return { status: 'error', info: 'Invalid time string. Dangling unit or number.' }; } var pairs = []; while (parts.length) { pairs.push([parts.shift(), parts.shift()]); } var seconds = 0; for (var _ref of pairs) { var _ref2 = (0, _slicedToArray2.default)(_ref, 2); var num = _ref2[0]; var interval = _ref2[1]; var val = Number(num); if (!Number.isInteger(val)) { return { status: 'error', info: `'${num}' is not an integer.` }; } switch (interval) { case 'yr': case 'yrs': case 'year': case 'years': seconds += val * 31536000; break; case 'wk': case 'wks': case 'week': case 'weeks': seconds += val * 604800; break; case 'd': case 'day': case 'days': seconds += val * 86400; break; case 'hr': case 'hrs': case 'hour': case 'hours': seconds += val * 3600; break; case 'min': case 'mins': case 'minute': case 'minutes': seconds += val * 60; break; case 'sec': case 'secs': case 'second': case 'seconds': seconds += val; break; default: return { status: 'error', info: `Invalid interval: '${interval}'` }; } } var milliseconds = seconds * 1000; if (future) { return { status: 'success', info: 'future', result: new Date(now.valueOf() + milliseconds) }; } else if (past) { return { status: 'success', info: 'past', result: new Date(now.valueOf() - milliseconds) }; } else { return { status: 'success', info: 'present', result: new Date(now.valueOf()) }; } } function matchesKeyConstraints(className, object, objects, key, constraints) { if (constraints === null) { return false; } if (key.indexOf('.') >= 0) { var keyComponents = key.split('.'); var subObjectKey = keyComponents[0]; var keyRemainder = keyComponents.slice(1).join('.'); return matchesKeyConstraints(className, object[subObjectKey] || {}, objects, keyRemainder, constraints); } var i; if (key === '$or') { for (i = 0; i < constraints.length; i++) { if (matchesQuery(className, object, objects, constraints[i])) { return true; } } return false; } if (key === '$and') { for (i = 0; i < constraints.length; i++) { if (!matchesQuery(className, object, objects, constraints[i])) { return false; } } return true; } if (key === '$nor') { for (i = 0; i < constraints.length; i++) { if (matchesQuery(className, object, objects, constraints[i])) { return false; } } return true; } if (key === '$relatedTo') { return false; } if (!/^[A-Za-z][0-9A-Za-z_]*$/.test(key)) { throw new ParseError(ParseError.INVALID_KEY_NAME, `Invalid Key: ${key}`); } if (typeof constraints !== 'object') { if (Array.isArray(object[key])) { return object[key].indexOf(constraints) > -1; } return object[key] === constraints; } var compareTo; if (constraints.__type) { if (constraints.__type === 'Pointer') { return equalObjectsGeneric(object[key], constraints, function (obj, ptr) { return typeof obj !== 'undefined' && ptr.className === obj.className && ptr.objectId === obj.objectId; }); } return equalObjectsGeneric(decode(object[key]), decode(constraints), equalObjects); } for (var condition in constraints) { compareTo = constraints[condition]; if (compareTo.__type) { compareTo = decode(compareTo); } if (compareTo['$relativeTime']) { var parserResult = relativeTimeToDate(compareTo['$relativeTime']); if (parserResult.status !== 'success') { throw new ParseError(ParseError.INVALID_JSON, `bad $relativeTime (${key}) value. ${parserResult.info}`); } compareTo = parserResult.result; } if (toString.call(compareTo) === '[object Date]' || typeof compareTo === 'string' && new Date(compareTo) !== 'Invalid Date' && !isNaN(new Date(compareTo))) { object[key] = new Date(object[key].iso ? object[key].iso : object[key]); } switch (condition) { case '$lt': if (object[key] >= compareTo) { return false; } break; case '$lte': if (object[key] > compareTo) { return false; } break; case '$gt': if (object[key] <= compareTo) { return false; } break; case '$gte': if (object[key] < compareTo) { return false; } break; case '$ne': if (equalObjects(object[key], compareTo)) { return false; } break; case '$in': if (!contains(compareTo, object[key])) { return false; } break; case '$nin': if (contains(compareTo, object[key])) { return false; } break; case '$all': for (i = 0; i < compareTo.length; i++) { if (object[key].indexOf(compareTo[i]) < 0) { return false; } } break; case '$exists': { var propertyExists = typeof object[key] !== 'undefined'; var existenceIsRequired = constraints['$exists']; if (typeof constraints['$exists'] !== 'boolean') { break; } if (!propertyExists && existenceIsRequired || propertyExists && !existenceIsRequired) { return false; } break; } case '$regex': { if (typeof compareTo === 'object') { return compareTo.test(object[key]); } var expString = ''; var escapeEnd = -2; var escapeStart = compareTo.indexOf('\\Q'); while (escapeStart > -1) { expString += compareTo.substring(escapeEnd + 2, escapeStart); escapeEnd = compareTo.indexOf('\\E', escapeStart); if (escapeEnd > -1) { expString += compareTo.substring(escapeStart + 2, escapeEnd).replace(/\\\\\\\\E/g, '\\E').replace(/\W/g, '\\$&'); } escapeStart = compareTo.indexOf('\\Q', escapeEnd); } expString += compareTo.substring(Math.max(escapeStart, escapeEnd + 2)); var modifiers = constraints.$options || ''; modifiers = modifiers.replace('x', '').replace('s', ''); var exp = new RegExp(expString, modifiers); if (!exp.test(object[key])) { return false; } break; } case '$nearSphere': { if (!compareTo || !object[key]) { return false; } var distance = compareTo.radiansTo(object[key]); var max = constraints.$maxDistance || Infinity; return distance <= max; } case '$within': { if (!compareTo || !object[key]) { return false; } var southWest = compareTo.$box[0]; var northEast = compareTo.$box[1]; if (southWest.latitude > northEast.latitude || southWest.longitude > northEast.longitude) { return false; } return object[key].latitude > southWest.latitude && object[key].latitude < northEast.latitude && object[key].longitude > southWest.longitude && object[key].longitude < northEast.longitude; } case '$options': break; case '$maxDistance': break; case '$select': { var subQueryObjects = objects.filter(function (obj, index, arr) { return matchesQuery(compareTo.query.className, obj, arr, compareTo.query.where); }); for (var _i = 0; _i < subQueryObjects.length; _i += 1) { var subObject = transformObject(subQueryObjects[_i]); return equalObjects(object[key], subObject[compareTo.key]); } return false; } case '$dontSelect': { var _subQueryObjects = objects.filter(function (obj, index, arr) { return matchesQuery(compareTo.query.className, obj, arr, compareTo.query.where); }); for (var _i2 = 0; _i2 < _subQueryObjects.length; _i2 += 1) { var _subObject = transformObject(_subQueryObjects[_i2]); return !equalObjects(object[key], _subObject[compareTo.key]); } return false; } case '$inQuery': { var _subQueryObjects2 = objects.filter(function (obj, index, arr) { return matchesQuery(compareTo.className, obj, arr, compareTo.where); }); for (var _i3 = 0; _i3 < _subQueryObjects2.length; _i3 += 1) { var _subObject2 = transformObject(_subQueryObjects2[_i3]); if (object[key].className === _subObject2.className && object[key].objectId === _subObject2.objectId) { return true; } } return false; } case '$notInQuery': { var _subQueryObjects3 = objects.filter(function (obj, index, arr) { return matchesQuery(compareTo.className, obj, arr, compareTo.where); }); for (var _i4 = 0; _i4 < _subQueryObjects3.length; _i4 += 1) { var _subObject3 = transformObject(_subQueryObjects3[_i4]); if (object[key].className === _subObject3.className && object[key].objectId === _subObject3.objectId) { return false; } } return true; } case '$containedBy': { for (var value of object[key]) { if (!contains(compareTo, value)) { return false; } } return true; } case '$geoWithin': { if (compareTo.$polygon) { var points = compareTo.$polygon.map(function (geoPoint) { return [geoPoint.latitude, geoPoint.longitude]; }); var polygon = new ParsePolygon(points); return polygon.containsPoint(object[key]); } if (compareTo.$centerSphere) { var _compareTo$$centerSph = (0, _slicedToArray2.default)(compareTo.$centerSphere, 2), WGS84Point = _compareTo$$centerSph[0], maxDistance = _compareTo$$centerSph[1]; var centerPoint = new ParseGeoPoint({ latitude: WGS84Point[1], longitude: WGS84Point[0] }); var point = new ParseGeoPoint(object[key]); var _distance = point.radiansTo(centerPoint); return _distance <= maxDistance; } break; } case '$geoIntersects': { var _polygon = new ParsePolygon(object[key].coordinates); var _point = new ParseGeoPoint(compareTo.$point); return _polygon.containsPoint(_point); } default: return false; } } return true; } function validateQuery(query) { var q = query; if (query.toJSON) { q = query.toJSON().where; } var specialQuerykeys = ['$and', '$or', '$nor', '_rperm', '_wperm', '_perishable_token', '_email_verify_token', '_email_verify_token_expires_at', '_account_lockout_expires_at', '_failed_login_count']; Object.keys(q).forEach(function (key) { if (q && q[key] && q[key].$regex) { if (typeof q[key].$options === 'string') { if (!q[key].$options.match(/^[imxs]+$/)) { throw new ParseError(ParseError.INVALID_QUERY, `Bad $options value for query: ${q[key].$options}`); } } } if (specialQuerykeys.indexOf(key) < 0 && !key.match(/^[a-zA-Z][a-zA-Z0-9_\.]*$/)) { throw new ParseError(ParseError.INVALID_KEY_NAME, `Invalid key name: ${key}`); } }); } var OfflineQuery = { matchesQuery: matchesQuery, validateQuery: validateQuery }; module.exports = OfflineQuery;