Refactor MongoTransform.js (#1823)

* Split transformAtom into transfromTopLevelAtom and transformInteriorAtom

* Use single param for inArray and inObject

* Tidyness in transformKeyValue

* Add transformInteriorKeyValue

* Remove update from tranformInteriorKeyValue

* Split out transform update

* Move validation out of transfromUpdate

* Remove force paramater from transformTopLevelAtom throw error after if necessary

* Turn transformKeyValue into transfromKey since it is only used for that purpose

* Remove unnecessary stuff from transformKey

* convert transformKey to use parse format schema

* interior keys fixes

* Add test for interior keys with special names

* Correct validation of inner keys
This commit is contained in:
Drew
2016-05-18 18:14:54 -07:00
parent d7d4699832
commit 4d4361451c
3 changed files with 207 additions and 179 deletions

View File

@@ -1437,4 +1437,36 @@ describe('miscellaneous', function() {
done(); done();
}); });
}); });
it('doesnt convert interior keys of objects that use special names', done => {
let obj = new Parse.Object('Obj');
obj.set('val', { createdAt: 'a', updatedAt: 1 });
obj.save()
.then(obj => new Parse.Query('Obj').get(obj.id))
.then(obj => {
expect(obj.get('val').createdAt).toEqual('a');
expect(obj.get('val').updatedAt).toEqual(1);
done();
});
});
it('bans interior keys containing . or $', done => {
new Parse.Object('Obj').save({innerObj: {'key with a $': 'fails'}})
.catch(error => {
expect(error.code).toEqual(Parse.Error.INVALID_NESTED_KEY);
return new Parse.Object('Obj').save({innerObj: {'key with a .': 'fails'}});
})
.catch(error => {
expect(error.code).toEqual(Parse.Error.INVALID_NESTED_KEY);
return new Parse.Object('Obj').save({innerObj: {innerInnerObj: {'key with $': 'fails'}}});
})
.catch(error => {
expect(error.code).toEqual(Parse.Error.INVALID_NESTED_KEY);
return new Parse.Object('Obj').save({innerObj: {innerInnerObj: {'key with .': 'fails'}}});
})
.catch(error => {
expect(error.code).toEqual(Parse.Error.INVALID_NESTED_KEY);
done();
})
});
}); });

View File

@@ -3,24 +3,23 @@ import _ from 'lodash';
var mongodb = require('mongodb'); var mongodb = require('mongodb');
var Parse = require('parse/node').Parse; var Parse = require('parse/node').Parse;
// Transforms a key-value pair from REST API form to Mongo form. const transformKey = (className, fieldName, schema) => {
// This is the main entry point for converting anything from REST form // Check if the schema is known since it's a built-in field.
// to Mongo form; no conversion should happen that doesn't pass switch(fieldName) {
// through this function. case 'objectId': return '_id';
// Schema should already be loaded. case 'createdAt': return '_created_at';
// case 'updatedAt': return '_updated_at';
// There are several options that can help transform: case 'sessionToken': return '_session_token';
// }
// update: true indicates that __op operators like Add and Delete
// in the value are converted to a mongo update form. Otherwise they are if (schema.fields[fieldName] && schema.fields[fieldName].__type == 'Pointer') {
// converted to static data. fieldName = '_p_' + fieldName;
// }
// Returns an object with {key: key, value: value}.
function transformKeyValue(schema, className, restKey, restValue, { return fieldName;
inArray, }
inObject,
update, const transformKeyValueForUpdate = (schema, className, restKey, restValue) => {
} = {}) {
// Check if the schema is known since it's a built-in field. // Check if the schema is known since it's a built-in field.
var key = restKey; var key = restKey;
var timeField = false; var timeField = false;
@@ -77,51 +76,60 @@ function transformKeyValue(schema, className, restKey, restValue, {
if (schema && schema.getExpectedType) { if (schema && schema.getExpectedType) {
expected = schema.getExpectedType(className, key); expected = schema.getExpectedType(className, key);
} }
if ((expected && expected.type == 'Pointer') || if ((expected && expected.type == 'Pointer') || (!expected && restValue && restValue.__type == 'Pointer')) {
(!expected && restValue && restValue.__type == 'Pointer')) {
key = '_p_' + key; key = '_p_' + key;
} }
var expectedTypeIsArray = (expected && expected.type === 'Array');
// Handle atomic values // Handle atomic values
var value = transformAtom(restValue, false, { inArray, inObject }); var value = transformTopLevelAtom(restValue);
if (value !== CannotTransform) { if (value !== CannotTransform) {
if (timeField && (typeof value === 'string')) { if (timeField && (typeof value === 'string')) {
value = new Date(value); value = new Date(value);
} }
return {key: key, value: value}; return {key, value};
}
// ACLs are handled before this method is called
// If an ACL key still exists here, something is wrong.
if (key === 'ACL') {
throw 'There was a problem transforming an ACL.';
} }
// Handle arrays // Handle arrays
if (restValue instanceof Array) { if (restValue instanceof Array) {
value = restValue.map((restObj) => { value = restValue.map(transformInteriorValue);
var out = transformKeyValue(schema, className, restKey, restObj, { inArray: true }); return {key, value};
return out.value;
});
return {key: key, value: value};
} }
// Handle update operators // Handle update operators
value = transformUpdateOperator(restValue, !update); if (typeof restValue === 'object' && '__op' in restValue) {
if (value !== CannotTransform) { return {key, value: transformUpdateOperator(restValue, false)};
return {key: key, value: value};
} }
// Handle normal objects by recursing // Handle normal objects by recursing
value = {}; if (Object.keys(restValue).some(key => key.includes('$') || key.includes('.'))) {
for (var subRestKey in restValue) { throw new Parse.Error(Parse.Error.INVALID_NESTED_KEY, "Nested keys should not contain the '$' or '.' characters");
var subRestValue = restValue[subRestKey];
var out = transformKeyValue(schema, className, subRestKey, subRestValue, { inObject: true });
// For recursed objects, keep the keys in rest format
value[subRestKey] = out.value;
} }
return {key: key, value: value}; value = _.mapValues(restValue, transformInteriorValue);
return {key, value};
}
const transformInteriorValue = restValue => {
if (typeof restValue === 'object' && Object.keys(restValue).some(key => key.includes('$') || key.includes('.'))) {
throw new Parse.Error(Parse.Error.INVALID_NESTED_KEY, "Nested keys should not contain the '$' or '.' characters");
}
// Handle atomic values
var value = transformInteriorAtom(restValue);
if (value !== CannotTransform) {
return value;
}
// Handle arrays
if (restValue instanceof Array) {
return restValue.map(transformInteriorValue);
}
// Handle update operators
if (typeof restValue === 'object' && '__op' in restValue) {
return transformUpdateOperator(restValue, true);
}
// Handle normal objects by recursing
return _.mapValues(restValue, transformInteriorValue);
} }
const valueAsDate = value => { const valueAsDate = value => {
@@ -205,8 +213,8 @@ function transformQueryKeyValue(className, key, value, { validate } = {}, schema
} }
// Handle atomic values // Handle atomic values
if (transformAtom(value, false) !== CannotTransform) { if (transformTopLevelAtom(value) !== CannotTransform) {
return {key, value: transformAtom(value, false)}; return {key, value: transformTopLevelAtom(value)};
} else { } else {
throw new Parse.Error(Parse.Error.INVALID_JSON, `You cannot use ${value} as a query parameter.`); throw new Parse.Error(Parse.Error.INVALID_JSON, `You cannot use ${value} as a query parameter.`);
} }
@@ -241,15 +249,15 @@ const parseObjectKeyValueToMongoObjectKeyValue = (
switch(restKey) { switch(restKey) {
case 'objectId': return {key: '_id', value: restValue}; case 'objectId': return {key: '_id', value: restValue};
case 'createdAt': case 'createdAt':
transformedValue = transformAtom(restValue, false); transformedValue = transformTopLevelAtom(restValue);
coercedToDate = typeof transformedValue === 'string' ? new Date(transformedValue) : transformedValue coercedToDate = typeof transformedValue === 'string' ? new Date(transformedValue) : transformedValue
return {key: '_created_at', value: coercedToDate}; return {key: '_created_at', value: coercedToDate};
case 'updatedAt': case 'updatedAt':
transformedValue = transformAtom(restValue, false); transformedValue = transformTopLevelAtom(restValue);
coercedToDate = typeof transformedValue === 'string' ? new Date(transformedValue) : transformedValue coercedToDate = typeof transformedValue === 'string' ? new Date(transformedValue) : transformedValue
return {key: '_updated_at', value: coercedToDate}; return {key: '_updated_at', value: coercedToDate};
case 'expiresAt': case 'expiresAt':
transformedValue = transformAtom(restValue, false); transformedValue = transformTopLevelAtom(restValue);
coercedToDate = typeof transformedValue === 'string' ? new Date(transformedValue) : transformedValue coercedToDate = typeof transformedValue === 'string' ? new Date(transformedValue) : transformedValue
return {key: 'expiresAt', value: coercedToDate}; return {key: 'expiresAt', value: coercedToDate};
case '_rperm': case '_rperm':
@@ -268,7 +276,7 @@ const parseObjectKeyValueToMongoObjectKeyValue = (
return {key: restKey, value: restValue}; return {key: restKey, value: restValue};
} }
} }
//skip straight to transformAtom for Bytes, they don't show up in the schema for some reason //skip straight to transformTopLevelAtom for Bytes, they don't show up in the schema for some reason
if (restValue && restValue.__type !== 'Bytes') { if (restValue && restValue.__type !== 'Bytes') {
//Note: We may not know the type of a field here, as the user could be saving (null) to a field //Note: We may not know the type of a field here, as the user could be saving (null) to a field
//That never existed before, meaning we can't infer the type. //That never existed before, meaning we can't infer the type.
@@ -278,7 +286,7 @@ const parseObjectKeyValueToMongoObjectKeyValue = (
} }
// Handle atomic values // Handle atomic values
var value = transformAtom(restValue, false, { inArray: false, inObject: false }); var value = transformTopLevelAtom(restValue);
if (value !== CannotTransform) { if (value !== CannotTransform) {
return {key: restKey, value: value}; return {key: restKey, value: value};
} }
@@ -291,28 +299,21 @@ const parseObjectKeyValueToMongoObjectKeyValue = (
// Handle arrays // Handle arrays
if (restValue instanceof Array) { if (restValue instanceof Array) {
value = restValue.map((restObj) => { value = restValue.map(transformInteriorValue);
var out = transformKeyValue(schema, className, restKey, restObj, { inArray: true });
return out.value;
});
return {key: restKey, value: value}; return {key: restKey, value: value};
} }
// Handle update operators. TODO: handle within Parse Server. DB adapter shouldn't see update operators in creates. // Handle update operators. TODO: handle within Parse Server. DB adapter shouldn't see update operators in creates.
value = transformUpdateOperator(restValue, true); if (typeof restValue === 'object' && '__op' in restValue) {
if (value !== CannotTransform) { return {key: restKey, value: transformUpdateOperator(restValue, true)};
return {key: restKey, value: value};
} }
// Handle normal objects by recursing // Handle normal objects by recursing
value = {}; if (Object.keys(restValue).some(key => key.includes('$') || key.includes('.'))) {
for (var subRestKey in restValue) { throw new Parse.Error(Parse.Error.INVALID_NESTED_KEY, "Nested keys should not contain the '$' or '.' characters");
var subRestValue = restValue[subRestKey];
var out = transformKeyValue(schema, className, subRestKey, subRestValue, { inObject: true });
// For recursed objects, keep the keys in rest format
value[subRestKey] = out.value;
} }
return {key: restKey, value: value}; value = _.mapValues(restValue, transformInteriorValue);
return {key: restKey, value};
} }
// Main exposed method to create new objects. // Main exposed method to create new objects.
@@ -362,13 +363,12 @@ function transformUpdate(schema, className, restUpdate) {
} }
for (var restKey in restUpdate) { for (var restKey in restUpdate) {
var out = transformKeyValue(schema, className, restKey, restUpdate[restKey], {update: true}); var out = transformKeyValueForUpdate(schema, className, restKey, restUpdate[restKey]);
// If the output value is an object with any $ keys, it's an // If the output value is an object with any $ keys, it's an
// operator that needs to be lifted onto the top level update // operator that needs to be lifted onto the top level update
// object. // object.
if (typeof out.value === 'object' && out.value !== null && if (typeof out.value === 'object' && out.value !== null && out.value.__op) {
out.value.__op) {
mongoUpdate[out.value.__op] = mongoUpdate[out.value.__op] || {}; mongoUpdate[out.value.__op] = mongoUpdate[out.value.__op] || {};
mongoUpdate[out.value.__op][out.key] = out.value.arg; mongoUpdate[out.value.__op][out.key] = out.value.arg;
} else { } else {
@@ -462,20 +462,33 @@ function untransformACL(mongoObject) {
// cannot perform a transformation // cannot perform a transformation
function CannotTransform() {} function CannotTransform() {}
const transformInteriorAtom = atom => {
// TODO: check validity harder for the __type-defined types
if (typeof atom === 'object' && atom && !(atom instanceof Date) && atom.__type === 'Pointer') {
return {
__type: 'Pointer',
className: atom.className,
objectId: atom.objectId
};
} else if (typeof atom === 'function' || typeof atom === 'symbol') {
throw new Parse.Error(Parse.Error.INVALID_JSON, `cannot transform value: ${atom}`);
} else if (DateCoder.isValidJSON(atom)) {
return DateCoder.JSONToDatabase(atom);
} else if (BytesCoder.isValidJSON(atom)) {
return BytesCoder.JSONToDatabase(atom);
} else {
return atom;
}
}
// Helper function to transform an atom from REST format to Mongo format. // Helper function to transform an atom from REST format to Mongo format.
// An atom is anything that can't contain other expressions. So it // An atom is anything that can't contain other expressions. So it
// includes things where objects are used to represent other // includes things where objects are used to represent other
// datatypes, like pointers and dates, but it does not include objects // datatypes, like pointers and dates, but it does not include objects
// or arrays with generic stuff inside. // or arrays with generic stuff inside.
// If options.inArray is true, we'll leave it in REST format.
// If options.inObject is true, we'll leave files in REST format.
// Raises an error if this cannot possibly be valid REST format. // Raises an error if this cannot possibly be valid REST format.
// Returns CannotTransform if it's just not an atom, or if force is // Returns CannotTransform if it's just not an atom
// true, throws an error. function transformTopLevelAtom(atom) {
function transformAtom(atom, force, {
inArray,
inObject,
} = {}) {
switch(typeof atom) { switch(typeof atom) {
case 'string': case 'string':
case 'number': case 'number':
@@ -499,14 +512,7 @@ function transformAtom(atom, force, {
// TODO: check validity harder for the __type-defined types // TODO: check validity harder for the __type-defined types
if (atom.__type == 'Pointer') { if (atom.__type == 'Pointer') {
if (!inArray && !inObject) { return `${atom.className}$${atom.objectId}`;
return `${atom.className}$${atom.objectId}`;
}
return {
__type: 'Pointer',
className: atom.className,
objectId: atom.objectId
};
} }
if (DateCoder.isValidJSON(atom)) { if (DateCoder.isValidJSON(atom)) {
return DateCoder.JSONToDatabase(atom); return DateCoder.JSONToDatabase(atom);
@@ -515,17 +521,10 @@ function transformAtom(atom, force, {
return BytesCoder.JSONToDatabase(atom); return BytesCoder.JSONToDatabase(atom);
} }
if (GeoPointCoder.isValidJSON(atom)) { if (GeoPointCoder.isValidJSON(atom)) {
return (inArray || inObject ? atom : GeoPointCoder.JSONToDatabase(atom)); return GeoPointCoder.JSONToDatabase(atom);
} }
if (FileCoder.isValidJSON(atom)) { if (FileCoder.isValidJSON(atom)) {
return (inArray || inObject ? atom : FileCoder.JSONToDatabase(atom)); return FileCoder.JSONToDatabase(atom);
}
if (inArray || inObject) {
return atom;
}
if (force) {
throw new Parse.Error(Parse.Error.INVALID_JSON, `bad atom: ${atom}`);
} }
return CannotTransform; return CannotTransform;
@@ -560,19 +559,24 @@ function transformConstraint(constraint, inArray) {
case '$exists': case '$exists':
case '$ne': case '$ne':
case '$eq': case '$eq':
answer[key] = transformAtom(constraint[key], true, answer[key] = inArray ? transformInteriorAtom(constraint[key]) : transformTopLevelAtom(constraint[key]);
{inArray: inArray}); if (answer[key] === CannotTransform) {
throw new Parse.Error(Parse.Error.INVALID_JSON, `bad atom: ${atom}`);
}
break; break;
case '$in': case '$in':
case '$nin': case '$nin':
var arr = constraint[key]; var arr = constraint[key];
if (!(arr instanceof Array)) { if (!(arr instanceof Array)) {
throw new Parse.Error(Parse.Error.INVALID_JSON, throw new Parse.Error(Parse.Error.INVALID_JSON, 'bad ' + key + ' value');
'bad ' + key + ' value');
} }
answer[key] = arr.map((v) => { answer[key] = arr.map(value => {
return transformAtom(v, true, { inArray: inArray }); let result = inArray ? transformInteriorAtom(value) : transformTopLevelAtom(value);
if (result === CannotTransform) {
throw new Parse.Error(Parse.Error.INVALID_JSON, `bad atom: ${atom}`);
}
return result;
}); });
break; break;
@@ -582,9 +586,7 @@ function transformConstraint(constraint, inArray) {
throw new Parse.Error(Parse.Error.INVALID_JSON, throw new Parse.Error(Parse.Error.INVALID_JSON,
'bad ' + key + ' value'); 'bad ' + key + ' value');
} }
answer[key] = arr.map((v) => { answer[key] = arr.map(transformInteriorAtom);
return transformAtom(v, true, { inArray: true });
});
break; break;
case '$regex': case '$regex':
@@ -667,14 +669,14 @@ function transformConstraint(constraint, inArray) {
// The output for a non-flattened operator is a hash with __op being // The output for a non-flattened operator is a hash with __op being
// the mongo op, and arg being the argument. // the mongo op, and arg being the argument.
// The output for a flattened operator is just a value. // The output for a flattened operator is just a value.
// Returns CannotTransform if this cannot transform it.
// Returns undefined if this should be a no-op. // Returns undefined if this should be a no-op.
function transformUpdateOperator(operator, flatten) {
if (typeof operator !== 'object' || !operator.__op) {
return CannotTransform;
}
switch(operator.__op) { function transformUpdateOperator({
__op,
amount,
objects,
}, flatten) {
switch(__op) {
case 'Delete': case 'Delete':
if (flatten) { if (flatten) {
return undefined; return undefined;
@@ -683,43 +685,36 @@ function transformUpdateOperator(operator, flatten) {
} }
case 'Increment': case 'Increment':
if (typeof operator.amount !== 'number') { if (typeof amount !== 'number') {
throw new Parse.Error(Parse.Error.INVALID_JSON, throw new Parse.Error(Parse.Error.INVALID_JSON, 'incrementing must provide a number');
'incrementing must provide a number');
} }
if (flatten) { if (flatten) {
return operator.amount; return amount;
} else { } else {
return {__op: '$inc', arg: operator.amount}; return {__op: '$inc', arg: amount};
} }
case 'Add': case 'Add':
case 'AddUnique': case 'AddUnique':
if (!(operator.objects instanceof Array)) { if (!(objects instanceof Array)) {
throw new Parse.Error(Parse.Error.INVALID_JSON, throw new Parse.Error(Parse.Error.INVALID_JSON, 'objects to add must be an array');
'objects to add must be an array');
} }
var toAdd = operator.objects.map((obj) => { var toAdd = objects.map(transformInteriorAtom);
return transformAtom(obj, true, { inArray: true });
});
if (flatten) { if (flatten) {
return toAdd; return toAdd;
} else { } else {
var mongoOp = { var mongoOp = {
Add: '$push', Add: '$push',
AddUnique: '$addToSet' AddUnique: '$addToSet'
}[operator.__op]; }[__op];
return {__op: mongoOp, arg: {'$each': toAdd}}; return {__op: mongoOp, arg: {'$each': toAdd}};
} }
case 'Remove': case 'Remove':
if (!(operator.objects instanceof Array)) { if (!(objects instanceof Array)) {
throw new Parse.Error(Parse.Error.INVALID_JSON, throw new Parse.Error(Parse.Error.INVALID_JSON, 'objects to remove must be an array');
'objects to remove must be an array');
} }
var toRemove = operator.objects.map((obj) => { var toRemove = objects.map(transformInteriorAtom);
return transformAtom(obj, true, { inArray: true });
});
if (flatten) { if (flatten) {
return []; return [];
} else { } else {
@@ -727,9 +722,7 @@ function transformUpdateOperator(operator, flatten) {
} }
default: default:
throw new Parse.Error( throw new Parse.Error(Parse.Error.COMMAND_UNAVAILABLE, `The ${__op} operator is not supported yet.`);
Parse.Error.COMMAND_UNAVAILABLE,
'the ' + operator.__op + ' op is not supported yet');
} }
} }
@@ -1037,7 +1030,7 @@ var FileCoder = {
}; };
module.exports = { module.exports = {
transformKeyValue, transformKey,
parseObjectToMongoObjectForCreate, parseObjectToMongoObjectForCreate,
transformUpdate, transformUpdate,
transformWhere, transformWhere,

View File

@@ -616,64 +616,67 @@ DatabaseController.prototype.find = function(className, query, {
let op = typeof query.objectId == 'string' && Object.keys(query).length === 1 ? 'get' : 'find'; let op = typeof query.objectId == 'string' && Object.keys(query).length === 1 ? 'get' : 'find';
return this.loadSchema() return this.loadSchema()
.then(schemaController => { .then(schemaController => {
if (sort) { return schemaController.getOneSchema(className)
mongoOptions.sort = {}; .catch(error => {
for (let fieldName in sort) { // If the schema doesn't exist, pretend it exists with no fields. This behaviour
// Parse.com treats queries on _created_at and _updated_at as if they were queries on createdAt and updatedAt, // will likely need revisiting.
// so duplicate that behaviour here. if (error === undefined) {
if (fieldName === '_created_at') { return { fields: {} };
fieldName = 'createdAt'; }
sort['createdAt'] = sort['_created_at']; throw error;
} else if (fieldName === '_updated_at') { })
fieldName = 'updatedAt'; .then(schema => {
sort['updatedAt'] = sort['_updated_at']; if (sort) {
} mongoOptions.sort = {};
for (let fieldName in sort) {
// Parse.com treats queries on _created_at and _updated_at as if they were queries on createdAt and updatedAt,
// so duplicate that behaviour here.
if (fieldName === '_created_at') {
fieldName = 'createdAt';
sort['createdAt'] = sort['_created_at'];
} else if (fieldName === '_updated_at') {
fieldName = 'updatedAt';
sort['updatedAt'] = sort['_updated_at'];
}
if (!SchemaController.fieldNameIsValid(fieldName)) { if (!SchemaController.fieldNameIsValid(fieldName)) {
throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, `Invalid field name: ${fieldName}.`); throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, `Invalid field name: ${fieldName}.`);
} }
const mongoKey = this.transform.transformKeyValue(schemaController, className, fieldName, null).key; if (fieldName.match(/^authData\.([a-zA-Z0-9_]+)\.id$/)) {
mongoOptions.sort[mongoKey] = sort[fieldName]; throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, `Cannot sort by ${fieldName}`);
} }
} const mongoKey = this.transform.transformKey(className, fieldName, schema);
return (isMaster ? Promise.resolve() : schemaController.validatePermission(className, aclGroup, op)) mongoOptions.sort[mongoKey] = sort[fieldName];
.then(() => this.reduceRelationKeys(className, query))
.then(() => this.reduceInRelation(className, query, schemaController))
.then(() => this.adapter.adaptiveCollection(className))
.then(collection => {
if (!isMaster) {
query = this.addPointerPermissions(schemaController, className, op, query, aclGroup);
}
if (!query) {
if (op == 'get') {
return Promise.reject(new Parse.Error(Parse.Error.OBJECT_NOT_FOUND,
'Object not found.'));
} else {
return Promise.resolve([]);
} }
} }
if (!isMaster) { return (isMaster ? Promise.resolve() : schemaController.validatePermission(className, aclGroup, op))
query = addReadACL(query, aclGroup); .then(() => this.reduceRelationKeys(className, query))
} .then(() => this.reduceInRelation(className, query, schemaController))
return schemaController.getOneSchema(className) .then(() => this.adapter.adaptiveCollection(className))
.catch(error => { .then(collection => {
// If the schema doesn't exist, pretend it exists with no fields. This behaviour if (!isMaster) {
// will likely need revisiting. query = this.addPointerPermissions(schemaController, className, op, query, aclGroup);
if (error === undefined) {
return { fields: {} };
} }
throw error; if (!query) {
}) if (op == 'get') {
.then(parseFormatSchema => { return Promise.reject(new Parse.Error(Parse.Error.OBJECT_NOT_FOUND,
let mongoWhere = this.transform.transformWhere(className, query, {}, parseFormatSchema); 'Object not found.'));
} else {
return Promise.resolve([]);
}
}
if (!isMaster) {
query = addReadACL(query, aclGroup);
}
let mongoWhere = this.transform.transformWhere(className, query, {}, schema);
if (count) { if (count) {
delete mongoOptions.limit; delete mongoOptions.limit;
return collection.count(mongoWhere, mongoOptions); return collection.count(mongoWhere, mongoOptions);
} else { } else {
return collection.find(mongoWhere, mongoOptions) return collection.find(mongoWhere, mongoOptions)
.then((mongoResults) => { .then(mongoResults => {
return mongoResults.map((r) => { return mongoResults.map(result => {
return this.untransformObject(schemaController, isMaster, aclGroup, className, r); return this.untransformObject(schemaController, isMaster, aclGroup, className, result);
}); });
}); });
} }