Remove direct mongo access from SchemaRouter.modify, Schema.deleteField.
This commit is contained in:
@@ -483,7 +483,7 @@ describe('Schema', () => {
|
|||||||
.then(schema => schema.deleteField('installationId', '_Installation'))
|
.then(schema => schema.deleteField('installationId', '_Installation'))
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
expect(error.code).toEqual(136);
|
expect(error.code).toEqual(136);
|
||||||
expect(error.error).toEqual('field installationId cannot be changed');
|
expect(error.message).toEqual('field installationId cannot be changed');
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -493,7 +493,7 @@ describe('Schema', () => {
|
|||||||
.then(schema => schema.deleteField('field', 'NoClass'))
|
.then(schema => schema.deleteField('field', 'NoClass'))
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
expect(error.code).toEqual(Parse.Error.INVALID_CLASS_NAME);
|
expect(error.code).toEqual(Parse.Error.INVALID_CLASS_NAME);
|
||||||
expect(error.error).toEqual('class NoClass does not exist');
|
expect(error.message).toEqual('Class NoClass does not exist.');
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -504,7 +504,7 @@ describe('Schema', () => {
|
|||||||
.then(schema => schema.deleteField('missingField', 'HasAllPOD'))
|
.then(schema => schema.deleteField('missingField', 'HasAllPOD'))
|
||||||
.fail(error => {
|
.fail(error => {
|
||||||
expect(error.code).toEqual(255);
|
expect(error.code).toEqual(255);
|
||||||
expect(error.error).toEqual('field missingField does not exist, cannot delete');
|
expect(error.message).toEqual('Field missingField does not exist, cannot delete.');
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -523,11 +523,11 @@ describe('Schema', () => {
|
|||||||
config.database.adapter.database.collection('test__Join:aRelation:HasPointersAndRelations', { strict: true }, (err, coll) => {
|
config.database.adapter.database.collection('test__Join:aRelation:HasPointersAndRelations', { strict: true }, (err, coll) => {
|
||||||
expect(err).toEqual(null);
|
expect(err).toEqual(null);
|
||||||
config.database.loadSchema()
|
config.database.loadSchema()
|
||||||
.then(schema => schema.deleteField('aRelation', 'HasPointersAndRelations', config.database.adapter.database, 'test_'))
|
.then(schema => schema.deleteField('aRelation', 'HasPointersAndRelations', config.database))
|
||||||
.then(() => config.database.adapter.database.collection('test__Join:aRelation:HasPointersAndRelations', { strict: true }, (err, coll) => {
|
.then(() => config.database.adapter.database.collection('test__Join:aRelation:HasPointersAndRelations', { strict: true }, (err, coll) => {
|
||||||
expect(err).not.toEqual(null);
|
expect(err).not.toEqual(null);
|
||||||
done();
|
done();
|
||||||
}))
|
}));
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
@@ -538,7 +538,7 @@ describe('Schema', () => {
|
|||||||
var obj2 = hasAllPODobject();
|
var obj2 = hasAllPODobject();
|
||||||
var p = Parse.Object.saveAll([obj1, obj2])
|
var p = Parse.Object.saveAll([obj1, obj2])
|
||||||
.then(() => config.database.loadSchema())
|
.then(() => config.database.loadSchema())
|
||||||
.then(schema => schema.deleteField('aString', 'HasAllPOD', config.database.adapter.database, 'test_'))
|
.then(schema => schema.deleteField('aString', 'HasAllPOD', config.database))
|
||||||
.then(() => new Parse.Query('HasAllPOD').get(obj1.id))
|
.then(() => new Parse.Query('HasAllPOD').get(obj1.id))
|
||||||
.then(obj1Reloaded => {
|
.then(obj1Reloaded => {
|
||||||
expect(obj1Reloaded.get('aString')).toEqual(undefined);
|
expect(obj1Reloaded.get('aString')).toEqual(undefined);
|
||||||
@@ -568,7 +568,7 @@ describe('Schema', () => {
|
|||||||
expect(obj1.get('aPointer').id).toEqual(obj1.id);
|
expect(obj1.get('aPointer').id).toEqual(obj1.id);
|
||||||
})
|
})
|
||||||
.then(() => config.database.loadSchema())
|
.then(() => config.database.loadSchema())
|
||||||
.then(schema => schema.deleteField('aPointer', 'NewClass', config.database.adapter.database, 'test_'))
|
.then(schema => schema.deleteField('aPointer', 'NewClass', config.database))
|
||||||
.then(() => new Parse.Query('NewClass').get(obj1.id))
|
.then(() => new Parse.Query('NewClass').get(obj1.id))
|
||||||
.then(obj1 => {
|
.then(obj1 => {
|
||||||
expect(obj1.get('aPointer')).toEqual(undefined);
|
expect(obj1.get('aPointer')).toEqual(undefined);
|
||||||
|
|||||||
@@ -369,7 +369,7 @@ describe('schemas', () => {
|
|||||||
}, (error, response, body) => {
|
}, (error, response, body) => {
|
||||||
expect(response.statusCode).toEqual(400);
|
expect(response.statusCode).toEqual(400);
|
||||||
expect(body.code).toEqual(Parse.Error.INVALID_CLASS_NAME);
|
expect(body.code).toEqual(Parse.Error.INVALID_CLASS_NAME);
|
||||||
expect(body.error).toEqual('class NoClass does not exist');
|
expect(body.error).toEqual('Class NoClass does not exist.');
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -390,7 +390,7 @@ describe('schemas', () => {
|
|||||||
}, (error, response, body) => {
|
}, (error, response, body) => {
|
||||||
expect(response.statusCode).toEqual(400);
|
expect(response.statusCode).toEqual(400);
|
||||||
expect(body.code).toEqual(255);
|
expect(body.code).toEqual(255);
|
||||||
expect(body.error).toEqual('field aString exists, cannot update');
|
expect(body.error).toEqual('Field aString exists, cannot update.');
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
@@ -412,7 +412,7 @@ describe('schemas', () => {
|
|||||||
}, (error, response, body) => {
|
}, (error, response, body) => {
|
||||||
expect(response.statusCode).toEqual(400);
|
expect(response.statusCode).toEqual(400);
|
||||||
expect(body.code).toEqual(255);
|
expect(body.code).toEqual(255);
|
||||||
expect(body.error).toEqual('field nonExistentKey does not exist, cannot delete');
|
expect(body.error).toEqual('Field nonExistentKey does not exist, cannot delete.');
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
// schemas.js
|
// schemas.js
|
||||||
|
|
||||||
var express = require('express'),
|
var express = require('express'),
|
||||||
Parse = require('parse/node').Parse,
|
Parse = require('parse/node').Parse,
|
||||||
Schema = require('../Schema');
|
Schema = require('../Schema');
|
||||||
|
|
||||||
import PromiseRouter from '../PromiseRouter';
|
import PromiseRouter from '../PromiseRouter';
|
||||||
|
|
||||||
@@ -49,10 +49,10 @@ function getAllSchemas(req) {
|
|||||||
return masterKeyRequiredResponse();
|
return masterKeyRequiredResponse();
|
||||||
}
|
}
|
||||||
return req.config.database.collection('_SCHEMA')
|
return req.config.database.collection('_SCHEMA')
|
||||||
.then(coll => coll.find({}).toArray())
|
.then(coll => coll.find({}).toArray())
|
||||||
.then(schemas => ({response: {
|
.then(schemas => ({response: {
|
||||||
results: schemas.map(mongoSchemaToSchemaAPIResponse)
|
results: schemas.map(mongoSchemaToSchemaAPIResponse)
|
||||||
}}));
|
}}));
|
||||||
}
|
}
|
||||||
|
|
||||||
function getOneSchema(req) {
|
function getOneSchema(req) {
|
||||||
@@ -60,15 +60,15 @@ function getOneSchema(req) {
|
|||||||
return masterKeyRequiredResponse();
|
return masterKeyRequiredResponse();
|
||||||
}
|
}
|
||||||
return req.config.database.collection('_SCHEMA')
|
return req.config.database.collection('_SCHEMA')
|
||||||
.then(coll => coll.findOne({'_id': req.params.className}))
|
.then(coll => coll.findOne({'_id': req.params.className}))
|
||||||
.then(schema => ({response: mongoSchemaToSchemaAPIResponse(schema)}))
|
.then(schema => ({response: mongoSchemaToSchemaAPIResponse(schema)}))
|
||||||
.catch(() => ({
|
.catch(() => ({
|
||||||
status: 400,
|
status: 400,
|
||||||
response: {
|
response: {
|
||||||
code: 103,
|
code: 103,
|
||||||
error: 'class ' + req.params.className + ' does not exist',
|
error: 'class ' + req.params.className + ' does not exist',
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
function createSchema(req) {
|
function createSchema(req) {
|
||||||
@@ -91,12 +91,12 @@ function createSchema(req) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
return req.config.database.loadSchema()
|
return req.config.database.loadSchema()
|
||||||
.then(schema => schema.addClassIfNotExists(className, req.body.fields))
|
.then(schema => schema.addClassIfNotExists(className, req.body.fields))
|
||||||
.then(result => ({ response: mongoSchemaToSchemaAPIResponse(result) }))
|
.then(result => ({ response: mongoSchemaToSchemaAPIResponse(result) }))
|
||||||
.catch(error => ({
|
.catch(error => ({
|
||||||
status: 400,
|
status: 400,
|
||||||
response: error,
|
response: error,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
function modifySchema(req) {
|
function modifySchema(req) {
|
||||||
@@ -112,75 +112,48 @@ function modifySchema(req) {
|
|||||||
var className = req.params.className;
|
var className = req.params.className;
|
||||||
|
|
||||||
return req.config.database.loadSchema()
|
return req.config.database.loadSchema()
|
||||||
.then(schema => {
|
.then(schema => {
|
||||||
if (!schema.data[className]) {
|
if (!schema.data[className]) {
|
||||||
return Promise.resolve({
|
throw new Parse.Error(Parse.Error.INVALID_CLASS_NAME, `Class ${req.params.className} does not exist.`);
|
||||||
status: 400,
|
}
|
||||||
response: {
|
|
||||||
code: Parse.Error.INVALID_CLASS_NAME,
|
let existingFields = schema.data[className];
|
||||||
error: 'class ' + req.params.className + ' does not exist',
|
Object.keys(submittedFields).forEach(name => {
|
||||||
|
let field = submittedFields[name];
|
||||||
|
if (existingFields[name] && field.__op !== 'Delete') {
|
||||||
|
throw new Parse.Error(255, `Field ${name} exists, cannot update.`);
|
||||||
|
}
|
||||||
|
if (!existingFields[name] && field.__op === 'Delete') {
|
||||||
|
throw new Parse.Error(255, `Field ${name} does not exist, cannot delete.`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
|
||||||
var existingFields = schema.data[className];
|
|
||||||
|
|
||||||
for (var submittedFieldName in submittedFields) {
|
let newSchema = Schema.buildMergedSchemaObject(existingFields, submittedFields);
|
||||||
if (existingFields[submittedFieldName] && submittedFields[submittedFieldName].__op !== 'Delete') {
|
let mongoObject = Schema.mongoSchemaFromFieldsAndClassName(newSchema, className);
|
||||||
return Promise.resolve({
|
if (!mongoObject.result) {
|
||||||
status: 400,
|
throw new Parse.Error(mongoObject.code, mongoObject.error);
|
||||||
response: {
|
|
||||||
code: 255,
|
|
||||||
error: 'field ' + submittedFieldName + ' exists, cannot update',
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!existingFields[submittedFieldName] && submittedFields[submittedFieldName].__op === 'Delete') {
|
// Finally we have checked to make sure the request is valid and we can start deleting fields.
|
||||||
return Promise.resolve({
|
// Do all deletions first, then a single save to _SCHEMA collection to handle all additions.
|
||||||
status: 400,
|
let deletionPromises = [];
|
||||||
response: {
|
Object.keys(submittedFields).forEach(submittedFieldName => {
|
||||||
code: 255,
|
if (submittedFields[submittedFieldName].__op === 'Delete') {
|
||||||
error: 'field ' + submittedFieldName + ' does not exist, cannot delete',
|
let promise = schema.deleteField(submittedFieldName, className, req.config.database);
|
||||||
}
|
deletionPromises.push(promise);
|
||||||
});
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var newSchema = Schema.buildMergedSchemaObject(existingFields, submittedFields);
|
|
||||||
var mongoObject = Schema.mongoSchemaFromFieldsAndClassName(newSchema, className);
|
|
||||||
if (!mongoObject.result) {
|
|
||||||
return Promise.resolve({
|
|
||||||
status: 400,
|
|
||||||
response: mongoObject,
|
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
// Finally we have checked to make sure the request is valid and we can start deleting fields.
|
return Promise.all(deletionPromises)
|
||||||
// Do all deletions first, then a single save to _SCHEMA collection to handle all additions.
|
.then(() => new Promise((resolve, reject) => {
|
||||||
var deletionPromises = []
|
schema.collection.update({_id: className}, mongoObject.result, {w: 1}, (err, docs) => {
|
||||||
Object.keys(submittedFields).forEach(submittedFieldName => {
|
if (err) {
|
||||||
if (submittedFields[submittedFieldName].__op === 'Delete') {
|
reject(err);
|
||||||
var promise = req.config.database.connect()
|
}
|
||||||
.then(() => schema.deleteField(
|
resolve({ response: mongoSchemaToSchemaAPIResponse(mongoObject.result)});
|
||||||
submittedFieldName,
|
})
|
||||||
className,
|
}));
|
||||||
req.config.database.adapter.database,
|
|
||||||
req.config.database.collectionPrefix
|
|
||||||
));
|
|
||||||
deletionPromises.push(promise);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return Promise.all(deletionPromises)
|
|
||||||
.then(() => new Promise((resolve, reject) => {
|
|
||||||
schema.collection.update({_id: className}, mongoObject.result, {w: 1}, (err, docs) => {
|
|
||||||
if (err) {
|
|
||||||
reject(err);
|
|
||||||
}
|
|
||||||
resolve({ response: mongoSchemaToSchemaAPIResponse(mongoObject.result)});
|
|
||||||
})
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// A helper function that removes all join tables for a schema. Returns a promise.
|
// A helper function that removes all join tables for a schema. Returns a promise.
|
||||||
|
|||||||
@@ -500,80 +500,47 @@ Schema.prototype.validateField = function(className, key, type, freeze) {
|
|||||||
|
|
||||||
// Passing the database and prefix is necessary in order to drop relation collections
|
// Passing the database and prefix is necessary in order to drop relation collections
|
||||||
// and remove fields from objects. Ideally the database would belong to
|
// and remove fields from objects. Ideally the database would belong to
|
||||||
// a database adapter and this fuction would close over it or access it via member.
|
// a database adapter and this function would close over it or access it via member.
|
||||||
Schema.prototype.deleteField = function(fieldName, className, database, prefix) {
|
Schema.prototype.deleteField = function(fieldName, className, database) {
|
||||||
if (!classNameIsValid(className)) {
|
if (!classNameIsValid(className)) {
|
||||||
return Promise.reject({
|
throw new Parse.Error(Parse.Error.INVALID_CLASS_NAME, invalidClassNameMessage(className));
|
||||||
code: Parse.Error.INVALID_CLASS_NAME,
|
|
||||||
error: invalidClassNameMessage(className),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!fieldNameIsValid(fieldName)) {
|
if (!fieldNameIsValid(fieldName)) {
|
||||||
return Promise.reject({
|
throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, `invalid field name: ${fieldName}`);
|
||||||
code: Parse.Error.INVALID_KEY_NAME,
|
|
||||||
error: 'invalid field name: ' + fieldName,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Don't allow deleting the default fields.
|
//Don't allow deleting the default fields.
|
||||||
if (!fieldNameIsValidForClass(fieldName, className)) {
|
if (!fieldNameIsValidForClass(fieldName, className)) {
|
||||||
return Promise.reject({
|
throw new Parse.Error(136, `field ${fieldName} cannot be changed`);
|
||||||
code: 136,
|
|
||||||
error: 'field ' + fieldName + ' cannot be changed',
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.reload()
|
return this.reload()
|
||||||
.then(schema => {
|
.then(schema => {
|
||||||
return schema.hasClass(className)
|
return schema.hasClass(className)
|
||||||
.then(hasClass => {
|
.then(hasClass => {
|
||||||
if (!hasClass) {
|
if (!hasClass) {
|
||||||
return Promise.reject({
|
throw new Parse.Error(Parse.Error.INVALID_CLASS_NAME, `Class ${className} does not exist.`);
|
||||||
code: Parse.Error.INVALID_CLASS_NAME,
|
}
|
||||||
error: 'class ' + className + ' does not exist',
|
if (!schema.data[className][fieldName]) {
|
||||||
});
|
throw new Parse.Error(255, `Field ${fieldName} does not exist, cannot delete.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!schema.data[className][fieldName]) {
|
if (schema.data[className][fieldName].startsWith('relation<')) {
|
||||||
return Promise.reject({
|
//For relations, drop the _Join table
|
||||||
code: 255,
|
return database.dropCollection(`_Join:${fieldName}:${className}`);
|
||||||
error: 'field ' + fieldName + ' does not exist, cannot delete',
|
}
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (schema.data[className][fieldName].startsWith('relation<')) {
|
// for non-relations, remove all the data.
|
||||||
//For relations, drop the _Join table
|
// This is necessary to ensure that the data is still gone if they add the same field.
|
||||||
return database.dropCollection(prefix + '_Join:' + fieldName + ':' + className)
|
return database.collection(className)
|
||||||
//Save the _SCHEMA object
|
.then(collection => {
|
||||||
|
var mongoFieldName = schema.data[className][fieldName].startsWith('*') ? '_p_' + fieldName : fieldName;
|
||||||
|
return collection.update({}, { "$unset": { [mongoFieldName] : null } }, { multi: true });
|
||||||
|
});
|
||||||
|
})
|
||||||
|
// Save the _SCHEMA object
|
||||||
.then(() => this.collection.update({ _id: className }, { $unset: {[fieldName]: null }}));
|
.then(() => this.collection.update({ _id: className }, { $unset: {[fieldName]: null }}));
|
||||||
} else {
|
|
||||||
//for non-relations, remove all the data. This is necessary to ensure that the data is still gone
|
|
||||||
//if they add the same field.
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
database.collection(prefix + className, (err, coll) => {
|
|
||||||
if (err) {
|
|
||||||
reject(err);
|
|
||||||
} else {
|
|
||||||
var mongoFieldName = schema.data[className][fieldName].startsWith('*') ?
|
|
||||||
'_p_' + fieldName :
|
|
||||||
fieldName;
|
|
||||||
return coll.update({}, {
|
|
||||||
"$unset": { [mongoFieldName] : null },
|
|
||||||
}, {
|
|
||||||
multi: true,
|
|
||||||
})
|
|
||||||
//Save the _SCHEMA object
|
|
||||||
.then(() => this.collection.update({ _id: className }, { $unset: {[fieldName]: null }}))
|
|
||||||
.then(resolve)
|
|
||||||
.catch(reject);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
};
|
||||||
}
|
|
||||||
|
|
||||||
// Given a schema promise, construct another schema promise that
|
// Given a schema promise, construct another schema promise that
|
||||||
// validates this field once the schema loads.
|
// validates this field once the schema loads.
|
||||||
|
|||||||
Reference in New Issue
Block a user