Begin isolating object creation code into an externalizable API. (#1569)
* Tidy up transformKeyValue * Specialize transformKeyValue for object creation * remove keys that never appear in creation requests * rename function * remove local var * early exit for simple keys * Refactor create * Force class creation when creating an object * Pass parameters to key value transformer * No need to check for array in this func * start using Parse Format schema in MongoTransform * Remove call to getExpectedType * add tests to ensure client can't see _PushStatus
This commit is contained in:
@@ -21,9 +21,13 @@ var Parse = require('parse/node').Parse;
|
||||
// validate: true indicates that key names are to be validated.
|
||||
//
|
||||
// Returns an object with {key: key, value: value}.
|
||||
export function transformKeyValue(schema, className, restKey, restValue, options) {
|
||||
options = options || {};
|
||||
|
||||
export function transformKeyValue(schema, className, restKey, restValue, {
|
||||
inArray,
|
||||
inObject,
|
||||
query,
|
||||
update,
|
||||
validate,
|
||||
} = {}) {
|
||||
// Check if the schema is known since it's a built-in field.
|
||||
var key = restKey;
|
||||
var timeField = false;
|
||||
@@ -62,7 +66,7 @@ export function transformKeyValue(schema, className, restKey, restValue, options
|
||||
return {key: key, value: restValue};
|
||||
break;
|
||||
case '$or':
|
||||
if (!options.query) {
|
||||
if (!query) {
|
||||
throw new Parse.Error(Parse.Error.INVALID_KEY_NAME,
|
||||
'you can only use $or in queries');
|
||||
}
|
||||
@@ -75,7 +79,7 @@ export function transformKeyValue(schema, className, restKey, restValue, options
|
||||
});
|
||||
return {key: '$or', value: mongoSubqueries};
|
||||
case '$and':
|
||||
if (!options.query) {
|
||||
if (!query) {
|
||||
throw new Parse.Error(Parse.Error.INVALID_KEY_NAME,
|
||||
'you can only use $and in queries');
|
||||
}
|
||||
@@ -91,7 +95,7 @@ export function transformKeyValue(schema, className, restKey, restValue, options
|
||||
// Other auth data
|
||||
var authDataMatch = key.match(/^authData\.([a-zA-Z0-9_]+)\.id$/);
|
||||
if (authDataMatch) {
|
||||
if (options.query) {
|
||||
if (query) {
|
||||
var provider = authDataMatch[1];
|
||||
// Special-case auth data.
|
||||
return {key: '_auth_data_'+provider+'.id', value: restValue};
|
||||
@@ -100,7 +104,7 @@ export function transformKeyValue(schema, className, restKey, restValue, options
|
||||
'can only query on ' + key);
|
||||
break;
|
||||
};
|
||||
if (options.validate && !key.match(/^[a-zA-Z][a-zA-Z0-9_\.]*$/)) {
|
||||
if (validate && !key.match(/^[a-zA-Z][a-zA-Z0-9_\.]*$/)) {
|
||||
throw new Parse.Error(Parse.Error.INVALID_KEY_NAME,
|
||||
'invalid key name: ' + key);
|
||||
}
|
||||
@@ -117,24 +121,24 @@ export function transformKeyValue(schema, className, restKey, restValue, options
|
||||
(!expected && restValue && restValue.__type == 'Pointer')) {
|
||||
key = '_p_' + key;
|
||||
}
|
||||
var inArray = (expected && expected.type === 'Array');
|
||||
var expectedTypeIsArray = (expected && expected.type === 'Array');
|
||||
|
||||
// Handle query constraints
|
||||
if (options.query) {
|
||||
value = transformConstraint(restValue, inArray);
|
||||
if (query) {
|
||||
value = transformConstraint(restValue, expectedTypeIsArray);
|
||||
if (value !== CannotTransform) {
|
||||
return {key: key, value: value};
|
||||
}
|
||||
}
|
||||
|
||||
if (inArray && options.query && !(restValue instanceof Array)) {
|
||||
if (expectedTypeIsArray && query && !(restValue instanceof Array)) {
|
||||
return {
|
||||
key: key, value: { '$all' : [restValue] }
|
||||
};
|
||||
}
|
||||
|
||||
// Handle atomic values
|
||||
var value = transformAtom(restValue, false, options);
|
||||
var value = transformAtom(restValue, false, { inArray, inObject });
|
||||
if (value !== CannotTransform) {
|
||||
if (timeField && (typeof value === 'string')) {
|
||||
value = new Date(value);
|
||||
@@ -150,7 +154,7 @@ export function transformKeyValue(schema, className, restKey, restValue, options
|
||||
|
||||
// Handle arrays
|
||||
if (restValue instanceof Array) {
|
||||
if (options.query) {
|
||||
if (query) {
|
||||
throw new Parse.Error(Parse.Error.INVALID_JSON,
|
||||
'cannot use array as query param');
|
||||
}
|
||||
@@ -162,7 +166,7 @@ export function transformKeyValue(schema, className, restKey, restValue, options
|
||||
}
|
||||
|
||||
// Handle update operators
|
||||
value = transformUpdateOperator(restValue, !options.update);
|
||||
value = transformUpdateOperator(restValue, !update);
|
||||
if (value !== CannotTransform) {
|
||||
return {key: key, value: value};
|
||||
}
|
||||
@@ -198,18 +202,114 @@ function transformWhere(schema, className, restWhere, options = {validate: true}
|
||||
return mongoWhere;
|
||||
}
|
||||
|
||||
const parseObjectKeyValueToMongoObjectKeyValue = (
|
||||
schema,
|
||||
className,
|
||||
restKey,
|
||||
restValue,
|
||||
parseFormatSchema
|
||||
) => {
|
||||
// Check if the schema is known since it's a built-in field.
|
||||
let transformedValue;
|
||||
let coercedToDate;
|
||||
switch(restKey) {
|
||||
case 'objectId': return {key: '_id', value: restValue};
|
||||
case '_created_at'://TODO: for some reason, _PushStatus is already transformed when it gets here. For now,
|
||||
// just pass the _created_at through. Later, we should make sure the push status doesn't get transformed inside Parse Server.
|
||||
case 'createdAt':
|
||||
transformedValue = transformAtom(restValue, false);
|
||||
coercedToDate = typeof transformedValue === 'string' ? new Date(transformedValue) : transformedValue
|
||||
return {key: '_created_at', value: coercedToDate};
|
||||
case 'updatedAt':
|
||||
transformedValue = transformAtom(restValue, false);
|
||||
coercedToDate = typeof transformedValue === 'string' ? new Date(transformedValue) : transformedValue
|
||||
return {key: '_updated_at', value: coercedToDate};
|
||||
case 'expiresAt':
|
||||
transformedValue = transformAtom(restValue, false);
|
||||
coercedToDate = typeof transformedValue === 'string' ? new Date(transformedValue) : transformedValue
|
||||
return {key: 'expiresAt', value: coercedToDate};
|
||||
case '_id': //TODO: for some reason, _PushStatus is already transformed when it gets here. For now,
|
||||
// just pass the ID through. Later, we should make sure the push status doesn't get transformed inside Parse Server.
|
||||
case '_rperm':
|
||||
case '_wperm':
|
||||
case '_email_verify_token':
|
||||
case '_hashed_password':
|
||||
case '_perishable_token': return {key: restKey, value: restValue};
|
||||
case 'sessionToken': return {key: '_session_token', value: restValue};
|
||||
default:
|
||||
// Auth data should have been transformed already
|
||||
if (restKey.match(/^authData\.([a-zA-Z0-9_]+)\.id$/)) {
|
||||
throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, 'can only query on ' + restKey);
|
||||
}
|
||||
// Trust that the auth data has been transformed and save it directly
|
||||
if (restKey.match(/^_auth_data_[a-zA-Z0-9_]+$/)) {
|
||||
return {key: restKey, value: restValue};
|
||||
}
|
||||
}
|
||||
//skip straight to transformAtom for Bytes, they don't show up in the schema for some reason
|
||||
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
|
||||
//That never existed before, meaning we can't infer the type.
|
||||
if (parseFormatSchema.fields[restKey] && parseFormatSchema.fields[restKey].type == 'Pointer' || restValue.__type == 'Pointer') {
|
||||
restKey = '_p_' + restKey;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle atomic values
|
||||
var value = transformAtom(restValue, false, { inArray: false, inObject: false });
|
||||
if (value !== CannotTransform) {
|
||||
return {key: restKey, value: value};
|
||||
}
|
||||
|
||||
// ACLs are handled before this method is called
|
||||
// If an ACL key still exists here, something is wrong.
|
||||
if (restKey === 'ACL') {
|
||||
throw 'There was a problem transforming an ACL.';
|
||||
}
|
||||
|
||||
// Handle arrays
|
||||
if (restValue instanceof Array) {
|
||||
value = restValue.map((restObj) => {
|
||||
var out = transformKeyValue(schema, className, restKey, restObj, { inArray: true });
|
||||
return out.value;
|
||||
});
|
||||
return {key: restKey, value: value};
|
||||
}
|
||||
|
||||
// Handle update operators. TODO: handle within Parse Server. DB adapter shouldn't see update operators in creates.
|
||||
value = transformUpdateOperator(restValue, true);
|
||||
if (value !== CannotTransform) {
|
||||
return {key: restKey, value: value};
|
||||
}
|
||||
|
||||
// Handle normal objects by recursing
|
||||
value = {};
|
||||
for (var subRestKey in restValue) {
|
||||
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};
|
||||
}
|
||||
|
||||
// Main exposed method to create new objects.
|
||||
// restCreate is the "create" clause in REST API form.
|
||||
// Returns the mongo form of the object.
|
||||
function parseObjectToMongoObject(schema, className, restCreate) {
|
||||
function parseObjectToMongoObjectForCreate(schema, className, restCreate, parseFormatSchema) {
|
||||
if (className == '_User') {
|
||||
restCreate = transformAuthData(restCreate);
|
||||
}
|
||||
var mongoCreate = transformACL(restCreate);
|
||||
for (var restKey in restCreate) {
|
||||
var out = transformKeyValue(schema, className, restKey, restCreate[restKey]);
|
||||
if (out.value !== undefined) {
|
||||
mongoCreate[out.key] = out.value;
|
||||
for (let restKey in restCreate) {
|
||||
let { key, value } = parseObjectKeyValueToMongoObjectKeyValue(
|
||||
schema,
|
||||
className,
|
||||
restKey,
|
||||
restCreate[restKey],
|
||||
parseFormatSchema
|
||||
);
|
||||
if (value !== undefined) {
|
||||
mongoCreate[key] = value;
|
||||
}
|
||||
}
|
||||
return mongoCreate;
|
||||
@@ -920,7 +1020,7 @@ var FileCoder = {
|
||||
|
||||
module.exports = {
|
||||
transformKey,
|
||||
parseObjectToMongoObject,
|
||||
parseObjectToMongoObjectForCreate,
|
||||
transformUpdate,
|
||||
transformWhere,
|
||||
transformSelect,
|
||||
|
||||
Reference in New Issue
Block a user