Cleanup delete schema (#1604)

* Some cleanup for deleting one schema

* tidyness

* Remove _allCollections as Parse Server doesn't need it.
This commit is contained in:
Drew
2016-04-25 11:47:57 -07:00
committed by Florent Vilmart
parent d14d451028
commit 234d0093ff
4 changed files with 52 additions and 51 deletions

View File

@@ -10,6 +10,21 @@ let MongoClient = mongodb.MongoClient;
const MongoSchemaCollectionName = '_SCHEMA'; const MongoSchemaCollectionName = '_SCHEMA';
const DefaultMongoURI = 'mongodb://localhost:27017/parse'; const DefaultMongoURI = 'mongodb://localhost:27017/parse';
const storageAdapterAllCollections = mongoAdapter => {
return mongoAdapter.connect()
.then(() => mongoAdapter.database.collections())
.then(collections => {
return collections.filter(collection => {
if (collection.namespace.match(/\.system\./)) {
return false;
}
// TODO: If you have one app with a collection prefix that happens to be a prefix of another
// apps prefix, this will go very very badly. We should fix that somehow.
return (collection.collectionName.indexOf(mongoAdapter._collectionPrefix) == 0);
});
});
}
export class MongoStorageAdapter { export class MongoStorageAdapter {
// Private // Private
_uri: string; _uri: string;
@@ -70,7 +85,10 @@ export class MongoStorageAdapter {
}); });
} }
dropCollection(className: string) { // Deletes a schema. Resolve if successful. If the schema doesn't
// exist, resolve with undefined. If schema exists, but can't be deleted for some other reason,
// reject with INTERNAL_SERVER_ERROR.
deleteOneSchema(className: string) {
return this.collection(this._collectionPrefix + className).then(collection => collection.drop()) return this.collection(this._collectionPrefix + className).then(collection => collection.drop())
.catch(error => { .catch(error => {
// 'ns not found' means collection was already gone. Ignore deletion attempt. // 'ns not found' means collection was already gone. Ignore deletion attempt.
@@ -81,18 +99,10 @@ export class MongoStorageAdapter {
}); });
} }
// Used for testing only right now. // Delete all data known to this adatper. Used for testing.
allCollections() { deleteAllSchemas() {
return this.connect().then(() => { return storageAdapterAllCollections(this)
return this.database.collections(); .then(collections => Promise.all(collections.map(collection => collection.drop())));
}).then(collections => {
return collections.filter(collection => {
if (collection.namespace.match(/\.system\./)) {
return false;
}
return (collection.collectionName.indexOf(this._collectionPrefix) == 0);
});
});
} }
// Remove the column and all the data. For Relations, the _Join collection is handled // Remove the column and all the data. For Relations, the _Join collection is handled

View File

@@ -381,11 +381,7 @@ DatabaseController.prototype.mongoFind = function(className, query, options = {}
// Returns a promise. // Returns a promise.
DatabaseController.prototype.deleteEverything = function() { DatabaseController.prototype.deleteEverything = function() {
this.schemaPromise = null; this.schemaPromise = null;
return this.adapter.deleteAllSchemas();
return this.adapter.allCollections().then(collections => {
let promises = collections.map(collection => collection.drop());
return Promise.all(promises);
});
}; };
// Finds the keys in a query. Returns a Set. REST format only // Finds the keys in a query. Returns a Set. REST format only
@@ -652,21 +648,19 @@ DatabaseController.prototype.find = function(className, query, {
DatabaseController.prototype.deleteSchema = function(className) { DatabaseController.prototype.deleteSchema = function(className) {
return this.collectionExists(className) return this.collectionExists(className)
.then(exist => { .then(exist => {
if (!exist) { if (!exist) {
return Promise.resolve(); return Promise.resolve();
}
return this.adapter.adaptiveCollection(className)
.then(collection => collection.count())
.then(count => {
if (count > 0) {
throw new Parse.Error(255, `Class ${className} is not empty, contains ${count} objects, cannot drop schema.`);
} }
return this.adapter.adaptiveCollection(className) return this.adapter.deleteOneSchema(className);
.then(collection => {
return collection.count()
.then(count => {
if (count > 0) {
throw new Parse.Error(255, `Class ${className} is not empty, contains ${count} objects, cannot drop schema.`);
}
return collection.drop();
})
})
}) })
});
} }
DatabaseController.prototype.addPointerPermissions = function(schema, className, operation, query, aclGroup = []) { DatabaseController.prototype.addPointerPermissions = function(schema, className, operation, query, aclGroup = []) {

View File

@@ -134,7 +134,7 @@ function validateCLP(perms, fields) {
} }
return; return;
} }
Object.keys(perms[operation]).forEach((key) => { Object.keys(perms[operation]).forEach((key) => {
verifyPermissionKey(key); verifyPermissionKey(key);
let perm = perms[operation][key]; let perm = perms[operation][key];
@@ -543,7 +543,7 @@ class SchemaController {
if (this.data[className][fieldName].type == 'Relation') { if (this.data[className][fieldName].type == 'Relation') {
//For relations, drop the _Join table //For relations, drop the _Join table
return database.adapter.deleteFields(className, [fieldName], []) return database.adapter.deleteFields(className, [fieldName], [])
.then(() => database.adapter.dropCollection(`_Join:${fieldName}:${className}`)); .then(() => database.adapter.deleteOneSchema(`_Join:${fieldName}:${className}`));
} }
const fieldNames = [fieldName]; const fieldNames = [fieldName];
@@ -632,7 +632,7 @@ class SchemaController {
found = true; found = true;
} }
} }
if (found) { if (found) {
return Promise.resolve(); return Promise.resolve();
} }
@@ -640,7 +640,7 @@ class SchemaController {
// No matching CLP, let's check the Pointer permissions // No matching CLP, let's check the Pointer permissions
// And handle those later // And handle those later
let permissionField = ['get', 'find'].indexOf(operation) > -1 ? 'readUserFields' : 'writeUserFields'; let permissionField = ['get', 'find'].indexOf(operation) > -1 ? 'readUserFields' : 'writeUserFields';
// Reject create when write lockdown // Reject create when write lockdown
if (permissionField == 'writeUserFields' && operation == 'create') { if (permissionField == 'writeUserFields' && operation == 'create') {
throw new Parse.Error(Parse.Error.OPERATION_FORBIDDEN, throw new Parse.Error(Parse.Error.OPERATION_FORBIDDEN,

View File

@@ -70,7 +70,7 @@ var removeJoinTables = (database, mongoSchema) => {
.filter(field => mongoSchema[field].startsWith('relation<')) .filter(field => mongoSchema[field].startsWith('relation<'))
.map(field => { .map(field => {
let collectionName = `_Join:${field}:${mongoSchema._id}`; let collectionName = `_Join:${field}:${mongoSchema._id}`;
return database.adapter.dropCollection(collectionName); return database.adapter.deleteOneSchema(collectionName);
}) })
); );
}; };
@@ -79,22 +79,19 @@ function deleteSchema(req) {
if (!SchemaController.classNameIsValid(req.params.className)) { if (!SchemaController.classNameIsValid(req.params.className)) {
throw new Parse.Error(Parse.Error.INVALID_CLASS_NAME, SchemaController.invalidClassNameMessage(req.params.className)); throw new Parse.Error(Parse.Error.INVALID_CLASS_NAME, SchemaController.invalidClassNameMessage(req.params.className));
} }
return req.config.database.deleteSchema(req.params.className) return req.config.database.deleteSchema(req.params.className)
.then(() => { .then(() => req.config.database.schemaCollection())
// We've dropped the collection now, so delete the item from _SCHEMA // We've dropped the collection now, so delete the item from _SCHEMA
// and clear the _Join collections // and clear the _Join collections
return req.config.database.schemaCollection() .then(coll => coll.findAndDeleteSchema(req.params.className))
.then(coll => coll.findAndDeleteSchema(req.params.className)) .then(document => {
.then(document => { if (document === null) {
if (document === null) { //tried to delete non-existent class
//tried to delete non-existent class return Promise.resolve();
return Promise.resolve(); }
} return removeJoinTables(req.config.database, document);
return removeJoinTables(req.config.database, document); })
}); .then(() => ({ response: {} }));
})
.then(() => ({ response: {} }));
} }
export class SchemasRouter extends PromiseRouter { export class SchemasRouter extends PromiseRouter {