Schema.js database agnostic (#1468)
* Schema.js database agnostic * nits
This commit is contained in:
@@ -685,9 +685,10 @@ describe('Schema', () => {
|
||||
.then(() => schema.reloadData())
|
||||
.then(() => {
|
||||
expect(schema['data']['NewClass']).toEqual({
|
||||
objectId: 'string',
|
||||
updatedAt: 'string',
|
||||
createdAt: 'string'
|
||||
objectId: { type: 'String' },
|
||||
updatedAt: { type: 'Date' },
|
||||
createdAt: { type: 'Date' },
|
||||
ACL: { type: 'ACL' }
|
||||
});
|
||||
done();
|
||||
});
|
||||
@@ -747,7 +748,7 @@ describe('Schema', () => {
|
||||
it('can merge schemas', done => {
|
||||
expect(Schema.buildMergedSchemaObject({
|
||||
_id: 'SomeClass',
|
||||
someType: 'number'
|
||||
someType: { type: 'Number' }
|
||||
}, {
|
||||
newType: {type: 'Number'}
|
||||
})).toEqual({
|
||||
@@ -760,8 +761,8 @@ describe('Schema', () => {
|
||||
it('can merge deletions', done => {
|
||||
expect(Schema.buildMergedSchemaObject({
|
||||
_id: 'SomeClass',
|
||||
someType: 'number',
|
||||
outDatedType: 'string',
|
||||
someType: { type: 'Number' },
|
||||
outDatedType: { type: 'String' },
|
||||
},{
|
||||
newType: {type: 'GeoPoint'},
|
||||
outDatedType: {__op: 'Delete'},
|
||||
@@ -775,16 +776,16 @@ describe('Schema', () => {
|
||||
it('ignore default field when merge with system class', done => {
|
||||
expect(Schema.buildMergedSchemaObject({
|
||||
_id: '_User',
|
||||
username: 'string',
|
||||
password: 'string',
|
||||
authData: 'object',
|
||||
email: 'string',
|
||||
emailVerified: 'boolean'
|
||||
username: { type: 'String' },
|
||||
password: { type: 'String' },
|
||||
authData: { type: 'Object' },
|
||||
email: { type: 'String' },
|
||||
emailVerified: { type: 'Boolean' },
|
||||
},{
|
||||
authData: {type: 'string'},
|
||||
customField: {type: 'string'},
|
||||
authData: { type: 'String' },
|
||||
customField: { type: 'String' },
|
||||
})).toEqual({
|
||||
customField: {type: 'string'}
|
||||
customField: { type: 'String' }
|
||||
});
|
||||
done();
|
||||
});
|
||||
|
||||
@@ -8,11 +8,11 @@ var dummySchema = {
|
||||
data: {},
|
||||
getExpectedType: function(className, key) {
|
||||
if (key == 'userPointer') {
|
||||
return '*_User';
|
||||
return { type: 'Pointer', targetClass: '_User' };
|
||||
} else if (key == 'picture') {
|
||||
return 'file';
|
||||
return { type: 'File' };
|
||||
} else if (key == 'location') {
|
||||
return 'geopoint';
|
||||
return { type: 'GeoPoint' };
|
||||
}
|
||||
return;
|
||||
},
|
||||
|
||||
@@ -93,6 +93,33 @@ function parseFieldTypeToMongoFieldType({ type, targetClass }) {
|
||||
}
|
||||
}
|
||||
|
||||
// Returns { code, error } if invalid, or { result }, an object
|
||||
// suitable for inserting into _SCHEMA collection, otherwise.
|
||||
function mongoSchemaFromFieldsAndClassNameAndCLP(fields, className, classLevelPermissions) {
|
||||
|
||||
let mongoObject = {
|
||||
_id: className,
|
||||
objectId: 'string',
|
||||
updatedAt: 'string',
|
||||
createdAt: 'string'
|
||||
};
|
||||
|
||||
for (let fieldName in fields) {
|
||||
mongoObject[fieldName] = parseFieldTypeToMongoFieldType(fields[fieldName]);
|
||||
}
|
||||
|
||||
if (typeof classLevelPermissions !== 'undefined') {
|
||||
mongoObject._metadata = mongoObject._metadata || {};
|
||||
if (!classLevelPermissions) {
|
||||
delete mongoObject._metadata.class_permissions;
|
||||
} else {
|
||||
mongoObject._metadata.class_permissions = classLevelPermissions;
|
||||
}
|
||||
}
|
||||
|
||||
return mongoObject;
|
||||
}
|
||||
|
||||
class MongoSchemaCollection {
|
||||
_collection: MongoCollection;
|
||||
|
||||
@@ -136,8 +163,9 @@ class MongoSchemaCollection {
|
||||
// later PR. Returns a promise that is expected to resolve with the newly created schema, in Parse format.
|
||||
// If the class already exists, returns a promise that rejects with undefined as the reason. If the collection
|
||||
// can't be added for a reason other than it already existing, requirements for rejection reason are TBD.
|
||||
addSchema(name: string, fields) {
|
||||
let mongoObject = _mongoSchemaObjectFromNameFields(name, fields);
|
||||
addSchema(name: string, fields, classLevelPermissions) {
|
||||
let mongoSchema = mongoSchemaFromFieldsAndClassNameAndCLP(fields, name, classLevelPermissions);
|
||||
let mongoObject = _mongoSchemaObjectFromNameFields(name, mongoSchema);
|
||||
return this._collection.insertOne(mongoObject)
|
||||
.then(result => mongoSchemaToParseSchema(result.ops[0]))
|
||||
.catch(error => {
|
||||
@@ -155,18 +183,27 @@ class MongoSchemaCollection {
|
||||
upsertSchema(name: string, query: string, update) {
|
||||
return this._collection.upsertOne(_mongoSchemaQueryFromNameQuery(name, query), update);
|
||||
}
|
||||
|
||||
updateField(className: string, fieldName: string, type: string) {
|
||||
// We don't have this field. Update the schema.
|
||||
// Note that we use the $exists guard and $set to avoid race
|
||||
// conditions in the database. This is important!
|
||||
let query = {};
|
||||
query[fieldName] = { '$exists': false };
|
||||
let update = {};
|
||||
if (typeof type === 'string') {
|
||||
type = {
|
||||
type: type
|
||||
}
|
||||
}
|
||||
update[fieldName] = parseFieldTypeToMongoFieldType(type);
|
||||
update = {'$set': update};
|
||||
return this.upsertSchema(className, query, update);
|
||||
}
|
||||
}
|
||||
|
||||
// Exported for testing reasons and because we haven't moved all mongo schema format
|
||||
// related logic into the database adapter yet.
|
||||
MongoSchemaCollection._TESTmongoSchemaToParseSchema = mongoSchemaToParseSchema
|
||||
|
||||
// Exported because we haven't moved all mongo schema format related logic
|
||||
// into the database adapter yet. We will remove this before too long.
|
||||
MongoSchemaCollection._DONOTUSEmongoFieldToParseSchemaField = mongoFieldToParseSchemaField
|
||||
|
||||
// Exported because we haven't moved all mongo schema format related logic
|
||||
// into the database adapter yet. We will remove this before too long.
|
||||
MongoSchemaCollection._DONOTUSEparseFieldTypeToMongoFieldType = parseFieldTypeToMongoFieldType;
|
||||
|
||||
export default MongoSchemaCollection
|
||||
|
||||
@@ -90,9 +90,8 @@ DatabaseController.prototype.loadSchema = function(acceptor = returnsTrue) {
|
||||
DatabaseController.prototype.redirectClassNameForKey = function(className, key) {
|
||||
return this.loadSchema().then((schema) => {
|
||||
var t = schema.getExpectedType(className, key);
|
||||
var match = t ? t.match(/^relation<(.*)>$/) : false;
|
||||
if (match) {
|
||||
return match[1];
|
||||
if (t.type == 'Relation') {
|
||||
return t.targetClass;
|
||||
} else {
|
||||
return className;
|
||||
}
|
||||
@@ -446,11 +445,10 @@ DatabaseController.prototype.reduceInRelation = function(className, query, schem
|
||||
let promises = Object.keys(query).map((key) => {
|
||||
if (query[key] && (query[key]['$in'] || query[key]['$ne'] || query[key]['$nin'] || query[key].__type == 'Pointer')) {
|
||||
let t = schema.getExpectedType(className, key);
|
||||
let match = t ? t.match(/^relation<(.*)>$/) : false;
|
||||
if (!match) {
|
||||
if (!t || t.type !== 'Relation') {
|
||||
return Promise.resolve(query);
|
||||
}
|
||||
let relatedClassName = match[1];
|
||||
let relatedClassName = t.targetClass;
|
||||
// Build the list of queries
|
||||
let queries = Object.keys(query[key]).map((constraintKey) => {
|
||||
let relatedIds;
|
||||
@@ -599,7 +597,6 @@ DatabaseController.prototype.find = function(className, query, options = {}) {
|
||||
if (options.limit) {
|
||||
mongoOptions.limit = options.limit;
|
||||
}
|
||||
|
||||
let isMaster = !('acl' in options);
|
||||
let aclGroup = options.acl || [];
|
||||
let acceptor = schema => schema.hasKeys(className, keysForQuery(query))
|
||||
|
||||
224
src/Schema.js
224
src/Schema.js
@@ -213,7 +213,6 @@ class Schema {
|
||||
this._collection = collection;
|
||||
|
||||
// this.data[className][fieldName] tells you the type of that field, in mongo format
|
||||
// TODO: use Parse format
|
||||
this.data = {};
|
||||
// this.perms[className][operation] tells you the acl-style permissions
|
||||
this.perms = {};
|
||||
@@ -229,14 +228,7 @@ class Schema {
|
||||
...(defaultColumns[schema.className] || {}),
|
||||
...schema.fields,
|
||||
}
|
||||
// ACL doesn't show up in mongo, it's implicit
|
||||
delete parseFormatSchema.ACL;
|
||||
// createdAt and updatedAt are wacky and have legacy baggage
|
||||
parseFormatSchema.createdAt = { type: 'String' };
|
||||
parseFormatSchema.updatedAt = { type: 'String' };
|
||||
//Necessary because we still use the mongo type internally here :(
|
||||
this.data[schema.className] = _.mapValues(parseFormatSchema, MongoSchemaCollection._DONOTUSEparseFieldTypeToMongoFieldType);
|
||||
|
||||
this.data[schema.className] = parseFormatSchema;
|
||||
this.perms[schema.className] = schema.classLevelPermissions;
|
||||
});
|
||||
});
|
||||
@@ -249,17 +241,16 @@ class Schema {
|
||||
// on success, and rejects with an error on fail. Ensure you
|
||||
// have authorization (master key, or client class creation
|
||||
// enabled) before calling this function.
|
||||
addClassIfNotExists(className, fields, classLevelPermissions) {
|
||||
if (this.data[className]) {
|
||||
throw new Parse.Error(Parse.Error.INVALID_CLASS_NAME, `Class ${className} already exists.`);
|
||||
addClassIfNotExists(className, fields = {}, classLevelPermissions) {
|
||||
var validationError = this.validateNewClass(className, fields, classLevelPermissions);
|
||||
if (validationError) {
|
||||
return Promise.reject(validationError);
|
||||
}
|
||||
|
||||
let mongoObject = mongoSchemaFromFieldsAndClassNameAndCLP(fields, className, classLevelPermissions);
|
||||
if (!mongoObject.result) {
|
||||
return Promise.reject(mongoObject);
|
||||
}
|
||||
|
||||
return this._collection.addSchema(className, mongoObject.result)
|
||||
return this._collection.addSchema(className, fields, classLevelPermissions)
|
||||
.then(res => {
|
||||
return Promise.resolve(res);
|
||||
})
|
||||
.catch(error => {
|
||||
if (error === undefined) {
|
||||
throw new Parse.Error(Parse.Error.INVALID_CLASS_NAME, `Class ${className} already exists.`);
|
||||
@@ -285,9 +276,9 @@ class Schema {
|
||||
});
|
||||
|
||||
let newSchema = buildMergedSchemaObject(existingFields, submittedFields);
|
||||
let mongoObject = mongoSchemaFromFieldsAndClassNameAndCLP(newSchema, className, classLevelPermissions);
|
||||
if (!mongoObject.result) {
|
||||
throw new Parse.Error(mongoObject.code, mongoObject.error);
|
||||
let validationError = this.validateSchemaData(className, newSchema, classLevelPermissions);
|
||||
if (validationError) {
|
||||
throw new Parse.Error(validationError.code, validationError.error);
|
||||
}
|
||||
|
||||
// Finally we have checked to make sure the request is valid and we can start deleting fields.
|
||||
@@ -302,12 +293,13 @@ class Schema {
|
||||
insertedFields.push(fieldName);
|
||||
}
|
||||
});
|
||||
|
||||
return Promise.all(deletePromises) // Delete Everything
|
||||
.then(() => this.reloadData()) // Reload our Schema, so we have all the new values
|
||||
.then(() => {
|
||||
let promises = insertedFields.map(fieldName => {
|
||||
const mongoType = mongoObject.result[fieldName];
|
||||
return this.validateField(className, fieldName, mongoType);
|
||||
const type = submittedFields[fieldName];
|
||||
return this.validateField(className, fieldName, type);
|
||||
});
|
||||
return Promise.all(promises);
|
||||
})
|
||||
@@ -315,7 +307,11 @@ class Schema {
|
||||
return this.setPermissions(className, classLevelPermissions)
|
||||
})
|
||||
//TODO: Move this logic into the database adapter
|
||||
.then(() => MongoSchemaCollection._TESTmongoSchemaToParseSchema(mongoObject.result));
|
||||
.then(() => {
|
||||
return { className: className,
|
||||
fields: this.data[className],
|
||||
classLevelPermissions: this.perms[className] }
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -363,6 +359,51 @@ class Schema {
|
||||
});
|
||||
}
|
||||
|
||||
validateNewClass(className, fields = {}, classLevelPermissions) {
|
||||
if (this.data[className]) {
|
||||
throw new Parse.Error(Parse.Error.INVALID_CLASS_NAME, `Class ${className} already exists.`);
|
||||
}
|
||||
if (!classNameIsValid(className)) {
|
||||
return {
|
||||
code: Parse.Error.INVALID_CLASS_NAME,
|
||||
error: invalidClassNameMessage(className),
|
||||
};
|
||||
}
|
||||
return this.validateSchemaData(className, fields, classLevelPermissions);
|
||||
}
|
||||
|
||||
validateSchemaData(className, fields, classLevelPermissions) {
|
||||
for (let fieldName in fields) {
|
||||
if (!fieldNameIsValid(fieldName)) {
|
||||
return {
|
||||
code: Parse.Error.INVALID_KEY_NAME,
|
||||
error: 'invalid field name: ' + fieldName,
|
||||
};
|
||||
}
|
||||
if (!fieldNameIsValidForClass(fieldName, className)) {
|
||||
return {
|
||||
code: 136,
|
||||
error: 'field ' + fieldName + ' cannot be added',
|
||||
};
|
||||
}
|
||||
const error = fieldTypeIsInvalid(fields[fieldName]);
|
||||
if (error) return { code: error.code, error: error.message };
|
||||
}
|
||||
|
||||
for (let fieldName in defaultColumns[className]) {
|
||||
fields[fieldName] = defaultColumns[className][fieldName];
|
||||
}
|
||||
|
||||
let geoPoints = Object.keys(fields).filter(key => fields[key] && fields[key].type === 'GeoPoint');
|
||||
if (geoPoints.length > 1) {
|
||||
return {
|
||||
code: Parse.Error.INCORRECT_TYPE,
|
||||
error: 'currently, only one GeoPoint field may exist in an object. Adding ' + geoPoints[1] + ' when ' + geoPoints[0] + ' already exists.',
|
||||
};
|
||||
}
|
||||
validateCLP(classLevelPermissions);
|
||||
}
|
||||
|
||||
// Sets the Class-level permissions for a given className, which must exist.
|
||||
setPermissions(className, perms) {
|
||||
if (typeof perms === 'undefined') {
|
||||
@@ -393,13 +434,17 @@ class Schema {
|
||||
if( fieldName.indexOf(".") > 0 ) {
|
||||
// subdocument key (x.y) => ok if x is of type 'object'
|
||||
fieldName = fieldName.split(".")[ 0 ];
|
||||
type = 'object';
|
||||
type = 'Object';
|
||||
}
|
||||
|
||||
let expected = this.data[className][fieldName];
|
||||
if (expected) {
|
||||
expected = (expected === 'map' ? 'object' : expected);
|
||||
if (expected === type) {
|
||||
expected = (expected === 'map' ? 'Object' : expected);
|
||||
if (expected.type && type.type
|
||||
&& expected.type == type.type
|
||||
&& expected.targetClass == type.targetClass) {
|
||||
return Promise.resolve(this);
|
||||
} else if (expected == type || expected.type == type) {
|
||||
return Promise.resolve(this);
|
||||
} else {
|
||||
throw new Parse.Error(
|
||||
@@ -419,10 +464,10 @@ class Schema {
|
||||
return Promise.resolve(this);
|
||||
}
|
||||
|
||||
if (type === 'geopoint') {
|
||||
if (type === 'GeoPoint') {
|
||||
// Make sure there are not other geopoint fields
|
||||
for (let otherKey in this.data[className]) {
|
||||
if (this.data[className][otherKey] === 'geopoint') {
|
||||
if (this.data[className][otherKey].type === 'GeoPoint') {
|
||||
throw new Parse.Error(
|
||||
Parse.Error.INCORRECT_TYPE,
|
||||
'there can only be one geopoint field in a class');
|
||||
@@ -430,15 +475,7 @@ class Schema {
|
||||
}
|
||||
}
|
||||
|
||||
// We don't have this field. Update the schema.
|
||||
// Note that we use the $exists guard and $set to avoid race
|
||||
// conditions in the database. This is important!
|
||||
let query = {};
|
||||
query[fieldName] = { '$exists': false };
|
||||
let update = {};
|
||||
update[fieldName] = type;
|
||||
update = {'$set': update};
|
||||
return this._collection.upsertSchema(className, query, update).then(() => {
|
||||
return this._collection.updateField(className, fieldName, type).then(() => {
|
||||
// The update succeeded. Reload the schema
|
||||
return this.reloadData();
|
||||
}, () => {
|
||||
@@ -487,7 +524,7 @@ class Schema {
|
||||
throw new Parse.Error(255, `Field ${fieldName} does not exist, cannot delete.`);
|
||||
}
|
||||
|
||||
if (this.data[className][fieldName].startsWith('relation<')) {
|
||||
if (this.data[className][fieldName].type == 'Relation') {
|
||||
//For relations, drop the _Join table
|
||||
return database.dropCollection(`_Join:${fieldName}:${className}`)
|
||||
.then(() => {
|
||||
@@ -504,7 +541,7 @@ class Schema {
|
||||
// This is necessary to ensure that the data is still gone if they add the same field.
|
||||
return database.adaptiveCollection(className)
|
||||
.then(collection => {
|
||||
let mongoFieldName = this.data[className][fieldName].startsWith('*') ? `_p_${fieldName}` : fieldName;
|
||||
let mongoFieldName = this.data[className][fieldName].type === 'Pointer' ? `_p_${fieldName}` : fieldName;
|
||||
return collection.updateMany({}, { "$unset": { [mongoFieldName]: null } });
|
||||
});
|
||||
})
|
||||
@@ -524,7 +561,7 @@ class Schema {
|
||||
continue;
|
||||
}
|
||||
let expected = getType(object[fieldName]);
|
||||
if (expected === 'geopoint') {
|
||||
if (expected === 'GeoPoint') {
|
||||
geocount++;
|
||||
}
|
||||
if (geocount > 1) {
|
||||
@@ -572,7 +609,6 @@ class Schema {
|
||||
Parse.Error.INCORRECT_TYPE,
|
||||
missingColumns[0]+' is required.');
|
||||
}
|
||||
|
||||
return Promise.resolve(this);
|
||||
}
|
||||
|
||||
@@ -629,13 +665,12 @@ class Schema {
|
||||
if (this.data && this.data[className]) {
|
||||
let classData = this.data[className];
|
||||
return Object.keys(classData).filter((field) => {
|
||||
return classData[field].startsWith('relation');
|
||||
return classData[field].type === 'Relation';
|
||||
}).reduce((memo, field) => {
|
||||
let type = classData[field];
|
||||
let className = type.slice('relation<'.length, type.length - 1);
|
||||
memo[field] = {
|
||||
__type: 'Relation',
|
||||
className: className
|
||||
className: type.targetClass
|
||||
};
|
||||
return memo;
|
||||
}, {});
|
||||
@@ -650,85 +685,22 @@ function load(collection) {
|
||||
return schema.reloadData().then(() => schema);
|
||||
}
|
||||
|
||||
// Returns { code, error } if invalid, or { result }, an object
|
||||
// suitable for inserting into _SCHEMA collection, otherwise.
|
||||
function mongoSchemaFromFieldsAndClassNameAndCLP(fields, className, classLevelPermissions) {
|
||||
if (!classNameIsValid(className)) {
|
||||
return {
|
||||
code: Parse.Error.INVALID_CLASS_NAME,
|
||||
error: invalidClassNameMessage(className),
|
||||
};
|
||||
}
|
||||
|
||||
for (let fieldName in fields) {
|
||||
if (!fieldNameIsValid(fieldName)) {
|
||||
return {
|
||||
code: Parse.Error.INVALID_KEY_NAME,
|
||||
error: 'invalid field name: ' + fieldName,
|
||||
};
|
||||
}
|
||||
if (!fieldNameIsValidForClass(fieldName, className)) {
|
||||
return {
|
||||
code: 136,
|
||||
error: 'field ' + fieldName + ' cannot be added',
|
||||
};
|
||||
}
|
||||
const error = fieldTypeIsInvalid(fields[fieldName]);
|
||||
if (error) return { code: error.code, error: error.message };
|
||||
}
|
||||
|
||||
let mongoObject = {
|
||||
_id: className,
|
||||
objectId: 'string',
|
||||
updatedAt: 'string',
|
||||
createdAt: 'string'
|
||||
};
|
||||
|
||||
for (let fieldName in defaultColumns[className]) {
|
||||
mongoObject[fieldName] = MongoSchemaCollection._DONOTUSEparseFieldTypeToMongoFieldType(defaultColumns[className][fieldName]);
|
||||
}
|
||||
|
||||
for (let fieldName in fields) {
|
||||
mongoObject[fieldName] = MongoSchemaCollection._DONOTUSEparseFieldTypeToMongoFieldType(fields[fieldName]);
|
||||
}
|
||||
|
||||
let geoPoints = Object.keys(mongoObject).filter(key => mongoObject[key] === 'geopoint');
|
||||
if (geoPoints.length > 1) {
|
||||
return {
|
||||
code: Parse.Error.INCORRECT_TYPE,
|
||||
error: 'currently, only one GeoPoint field may exist in an object. Adding ' + geoPoints[1] + ' when ' + geoPoints[0] + ' already exists.',
|
||||
};
|
||||
}
|
||||
|
||||
validateCLP(classLevelPermissions);
|
||||
if (typeof classLevelPermissions !== 'undefined') {
|
||||
mongoObject._metadata = mongoObject._metadata || {};
|
||||
if (!classLevelPermissions) {
|
||||
delete mongoObject._metadata.class_permissions;
|
||||
} else {
|
||||
mongoObject._metadata.class_permissions = classLevelPermissions;
|
||||
}
|
||||
}
|
||||
|
||||
return { result: mongoObject };
|
||||
}
|
||||
|
||||
// Builds a new schema (in schema API response format) out of an
|
||||
// existing mongo schema + a schemas API put request. This response
|
||||
// does not include the default fields, as it is intended to be passed
|
||||
// to mongoSchemaFromFieldsAndClassName. No validation is done here, it
|
||||
// is done in mongoSchemaFromFieldsAndClassName.
|
||||
function buildMergedSchemaObject(mongoObject, putRequest) {
|
||||
function buildMergedSchemaObject(existingFields, putRequest) {
|
||||
let newSchema = {};
|
||||
let sysSchemaField = Object.keys(defaultColumns).indexOf(mongoObject._id) === -1 ? [] : Object.keys(defaultColumns[mongoObject._id]);
|
||||
for (let oldField in mongoObject) {
|
||||
let sysSchemaField = Object.keys(defaultColumns).indexOf(existingFields._id) === -1 ? [] : Object.keys(defaultColumns[existingFields._id]);
|
||||
for (let oldField in existingFields) {
|
||||
if (oldField !== '_id' && oldField !== 'ACL' && oldField !== 'updatedAt' && oldField !== 'createdAt' && oldField !== 'objectId') {
|
||||
if (sysSchemaField.length > 0 && sysSchemaField.indexOf(oldField) !== -1) {
|
||||
continue;
|
||||
}
|
||||
let fieldIsDeleted = putRequest[oldField] && putRequest[oldField].__op === 'Delete'
|
||||
if (!fieldIsDeleted) {
|
||||
newSchema[oldField] = MongoSchemaCollection._DONOTUSEmongoFieldToParseSchemaField(mongoObject[oldField]);
|
||||
newSchema[oldField] = existingFields[oldField];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -768,9 +740,11 @@ function getType(obj) {
|
||||
let type = typeof obj;
|
||||
switch(type) {
|
||||
case 'boolean':
|
||||
return 'Boolean';
|
||||
case 'string':
|
||||
return 'String';
|
||||
case 'number':
|
||||
return type;
|
||||
return 'Number';
|
||||
case 'map':
|
||||
case 'object':
|
||||
if (!obj) {
|
||||
@@ -790,25 +764,28 @@ function getType(obj) {
|
||||
// Returns null if the type is unknown.
|
||||
function getObjectType(obj) {
|
||||
if (obj instanceof Array) {
|
||||
return 'array';
|
||||
return 'Array';
|
||||
}
|
||||
if (obj.__type){
|
||||
switch(obj.__type) {
|
||||
case 'Pointer' :
|
||||
if(obj.className) {
|
||||
return '*' + obj.className;
|
||||
return {
|
||||
type: 'Pointer',
|
||||
targetClass: obj.className
|
||||
}
|
||||
}
|
||||
case 'File' :
|
||||
if(obj.name) {
|
||||
return 'file';
|
||||
return 'File';
|
||||
}
|
||||
case 'Date' :
|
||||
if(obj.iso) {
|
||||
return 'date';
|
||||
return 'Date';
|
||||
}
|
||||
case 'GeoPoint' :
|
||||
if(obj.latitude != null && obj.longitude != null) {
|
||||
return 'geopoint';
|
||||
return 'GeoPoint';
|
||||
}
|
||||
case 'Bytes' :
|
||||
if(obj.base64) {
|
||||
@@ -824,23 +801,26 @@ function getObjectType(obj) {
|
||||
if (obj.__op) {
|
||||
switch(obj.__op) {
|
||||
case 'Increment':
|
||||
return 'number';
|
||||
return 'Number';
|
||||
case 'Delete':
|
||||
return null;
|
||||
case 'Add':
|
||||
case 'AddUnique':
|
||||
case 'Remove':
|
||||
return 'array';
|
||||
return 'Array';
|
||||
case 'AddRelation':
|
||||
case 'RemoveRelation':
|
||||
return 'relation<' + obj.objects[0].className + '>';
|
||||
return {
|
||||
type: 'Relation',
|
||||
targetClass: obj.objects[0].className
|
||||
}
|
||||
case 'Batch':
|
||||
return getObjectType(obj.ops[0]);
|
||||
default:
|
||||
throw 'unexpected op: ' + obj.__op;
|
||||
}
|
||||
}
|
||||
return 'object';
|
||||
return 'Object';
|
||||
}
|
||||
|
||||
export {
|
||||
|
||||
@@ -87,7 +87,6 @@ function del(config, auth, className, objectId) {
|
||||
// Returns a promise for a {response, status, location} object.
|
||||
function create(config, auth, className, restObject) {
|
||||
enforceRoleSecurity('create', className, auth);
|
||||
|
||||
var write = new RestWrite(config, auth, className, null, restObject);
|
||||
return write.execute();
|
||||
}
|
||||
|
||||
@@ -115,11 +115,11 @@ export function transformKeyValue(schema, className, restKey, restValue, options
|
||||
if (schema && schema.getExpectedType) {
|
||||
expected = schema.getExpectedType(className, key);
|
||||
}
|
||||
if ((expected && expected[0] == '*') ||
|
||||
if ((expected && expected.type == 'Pointer') ||
|
||||
(!expected && restValue && restValue.__type == 'Pointer')) {
|
||||
key = '_p_' + key;
|
||||
}
|
||||
var inArray = (expected === 'array');
|
||||
var inArray = (expected && expected.type === 'Array');
|
||||
|
||||
// Handle query constraints
|
||||
if (options.query) {
|
||||
@@ -713,7 +713,7 @@ function untransformObject(schema, className, mongoObject, isNestedObject = fals
|
||||
className, newKey);
|
||||
break;
|
||||
}
|
||||
if (expected && expected[0] != '*') {
|
||||
if (expected && expected.type !== 'Pointer') {
|
||||
log.info('transform.js', 'Found a pointer in a non-pointer column, dropping it.', className, key);
|
||||
break;
|
||||
}
|
||||
@@ -721,7 +721,7 @@ function untransformObject(schema, className, mongoObject, isNestedObject = fals
|
||||
break;
|
||||
}
|
||||
var objData = mongoObject[key].split('$');
|
||||
var newClass = (expected ? expected.substring(1) : objData[0]);
|
||||
var newClass = (expected ? expected.targetClass : objData[0]);
|
||||
if (objData[0] !== newClass) {
|
||||
throw 'pointer to incorrect className';
|
||||
}
|
||||
@@ -736,11 +736,11 @@ function untransformObject(schema, className, mongoObject, isNestedObject = fals
|
||||
} else {
|
||||
var expectedType = schema.getExpectedType(className, key);
|
||||
var value = mongoObject[key];
|
||||
if (expectedType === 'file' && FileCoder.isValidDatabaseObject(value)) {
|
||||
if (expectedType && expectedType.type === 'File' && FileCoder.isValidDatabaseObject(value)) {
|
||||
restObject[key] = FileCoder.databaseToJSON(value);
|
||||
break;
|
||||
}
|
||||
if (expectedType === 'geopoint' && GeoPointCoder.isValidDatabaseObject(value)) {
|
||||
if (expectedType && expectedType.type === 'GeoPoint' && GeoPointCoder.isValidDatabaseObject(value)) {
|
||||
restObject[key] = GeoPointCoder.databaseToJSON(value);
|
||||
break;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user