Move "No two geopoints" logic into mongo adapter (#1491)

* Move "No two geopoints" logic into mongo adapter

* Semicolon
This commit is contained in:
Drew
2016-04-18 17:10:30 -07:00
parent cecb2a1cb1
commit 0708af17d7
4 changed files with 51 additions and 39 deletions

View File

@@ -85,8 +85,8 @@ describe('Parse.GeoPoint testing', () => {
equal(results.length, 3); equal(results.length, 3);
done(); done();
}, (err) => { }, (err) => {
console.log(err); fail("Couldn't query GeoPoint");
fail(); fail(err)
}); });
}); });

View File

@@ -179,21 +179,45 @@ class MongoSchemaCollection {
return this._collection.upsertOne(_mongoSchemaQueryFromNameQuery(name, query), update); return this._collection.upsertOne(_mongoSchemaQueryFromNameQuery(name, query), update);
} }
updateField(className: string, fieldName: string, type: string) { // Add a field to the schema. If database does not support the field
// We don't have this field. Update the schema. // type (e.g. mongo doesn't support more than one GeoPoint in a class) reject with an "Incorrect Type"
// Note that we use the $exists guard and $set to avoid race // Parse error with a desciptive message. If the field already exists, this function must
// conditions in the database. This is important! // not modify the schema, and must reject with an error. Exact error format is TBD. If this function
let query = {}; // is called for a class that doesn't exist, this function must create that class.
query[fieldName] = { '$exists': false };
let update = {}; // TODO: throw an error if an unsupported field type is passed. Deciding whether a type is supported
if (typeof type === 'string') { // should be the job of the adapter. Some adapters may not support GeoPoint at all. Others may
type = { // Support additional types that Mongo doesn't, like Money, or something.
type: type
// TODO: don't spend an extra query on finding the schema if the type we are trying to add isn't a GeoPoint.
addFieldIfNotExists(className: string, fieldName: string, type: string) {
return this.findSchema(className)
.then(schema => {
// The schema exists. Check for existing GeoPoints.
if (type.type === 'GeoPoint') {
// Make sure there are not other geopoint fields
if (Object.keys(schema.fields).some(existingField => schema.fields[existingField].type === 'GeoPoint')) {
return Promise.reject(new Parse.Error(Parse.Error.INCORRECT_TYPE, 'MongoDB only supports one GeoPoint field in a class.'));
}
} }
} return Promise.resolve();
update[fieldName] = parseFieldTypeToMongoFieldType(type); }, error => {
update = {'$set': update}; // If error is undefined, the schema doesn't exist, and we can create the schema with the field.
return this.upsertSchema(className, query, update); // If some other error, reject with it.
if (error === undefined) {
return Promise.resolve();
}
throw Promise.reject(error);
})
.then(() => {
// We use $exists and $set to avoid overwriting the field type if it
// already exists. (it could have added inbetween the last query and the update)
return this.upsertSchema(
className,
{ [fieldName]: { '$exists': false } },
{ '$set' : { [fieldName]: parseFieldTypeToMongoFieldType(type) } }
);
});
} }
get transform() { get transform() {

View File

@@ -346,23 +346,20 @@ function CannotTransform() {}
// 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, or if force is
// true, throws an error. // true, throws an error.
function transformAtom(atom, force, options) { function transformAtom(atom, force, {
options = options || {}; inArray,
var inArray = options.inArray; inObject,
var inObject = options.inObject; } = {}) {
switch(typeof atom) { switch(typeof atom) {
case 'string': case 'string':
case 'number': case 'number':
case 'boolean': case 'boolean':
return atom; return atom;
case 'undefined': case 'undefined':
return atom; return atom;
case 'symbol': case 'symbol':
case 'function': case 'function':
throw new Parse.Error(Parse.Error.INVALID_JSON, throw new Parse.Error(Parse.Error.INVALID_JSON, `cannot transform value: ${atom}`);
'cannot transform value: ' + atom);
case 'object': case 'object':
if (atom instanceof Date) { if (atom instanceof Date) {
// Technically dates are not rest format, but, it seems pretty // Technically dates are not rest format, but, it seems pretty
@@ -377,7 +374,7 @@ function transformAtom(atom, force, options) {
// 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) { if (!inArray && !inObject) {
return atom.className + '$' + atom.objectId; return `${atom.className}$${atom.objectId}`;
} }
return { return {
__type: 'Pointer', __type: 'Pointer',
@@ -402,15 +399,13 @@ function transformAtom(atom, force, options) {
} }
if (force) { if (force) {
throw new Parse.Error(Parse.Error.INVALID_JSON, throw new Parse.Error(Parse.Error.INVALID_JSON, `bad atom: ${atom}`);
'bad atom: ' + atom);
} }
return CannotTransform; return CannotTransform;
default: default:
// I don't think typeof can ever let us get here // I don't think typeof can ever let us get here
throw new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, throw new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, `really did not expect value: ${atom}`);
'really did not expect value: ' + atom);
} }
} }

View File

@@ -479,18 +479,11 @@ class SchemaController {
return Promise.resolve(this); return Promise.resolve(this);
} }
if (type === 'GeoPoint') { if (typeof type === 'string') {
// Make sure there are not other geopoint fields type = { type };
for (let otherKey in this.data[className]) {
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');
}
}
} }
return this._collection.updateField(className, fieldName, type).then(() => { return this._collection.addFieldIfNotExists(className, fieldName, type).then(() => {
// The update succeeded. Reload the schema // The update succeeded. Reload the schema
return this.reloadData(); return this.reloadData();
}, () => { }, () => {