Fixes issue affecting deleting multiple fields of a Schema (#3735)
This commit is contained in:
committed by
Florent Vilmart
parent
2a5c20376a
commit
5e14147676
@@ -727,6 +727,47 @@ describe('schemas', () => {
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('lets you delete multiple fields and check schema', done => {
|
||||||
|
var simpleOneObject = () => {
|
||||||
|
var obj = new Parse.Object('SimpleOne');
|
||||||
|
obj.set('aNumber', 5);
|
||||||
|
obj.set('aString', 'string');
|
||||||
|
obj.set('aBool', true);
|
||||||
|
return obj;
|
||||||
|
};
|
||||||
|
|
||||||
|
simpleOneObject().save()
|
||||||
|
.then(() => {
|
||||||
|
request.put({
|
||||||
|
url: 'http://localhost:8378/1/schemas/SimpleOne',
|
||||||
|
headers: masterKeyHeaders,
|
||||||
|
json: true,
|
||||||
|
body: {
|
||||||
|
fields: {
|
||||||
|
aString: {__op: 'Delete'},
|
||||||
|
aNumber: {__op: 'Delete'},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, (error, response, body) => {
|
||||||
|
expect(body).toEqual({
|
||||||
|
className: 'SimpleOne',
|
||||||
|
fields: {
|
||||||
|
//Default fields
|
||||||
|
ACL: {type: 'ACL'},
|
||||||
|
createdAt: {type: 'Date'},
|
||||||
|
updatedAt: {type: 'Date'},
|
||||||
|
objectId: {type: 'String'},
|
||||||
|
//Custom fields
|
||||||
|
aBool: {type: 'Boolean'},
|
||||||
|
},
|
||||||
|
classLevelPermissions: defaultClassLevelPermissions
|
||||||
|
});
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it_exclude_dbs(['postgres'])('lets you delete multiple fields and add fields', done => {
|
it_exclude_dbs(['postgres'])('lets you delete multiple fields and add fields', done => {
|
||||||
var obj1 = hasAllPODobject();
|
var obj1 = hasAllPODobject();
|
||||||
obj1.save()
|
obj1.save()
|
||||||
|
|||||||
@@ -623,7 +623,7 @@ export class PostgresStorageAdapter {
|
|||||||
const values = [className, ...fieldNames];
|
const values = [className, ...fieldNames];
|
||||||
const columns = fieldNames.map((name, idx) => {
|
const columns = fieldNames.map((name, idx) => {
|
||||||
return `$${idx + 2}:name`;
|
return `$${idx + 2}:name`;
|
||||||
}).join(',');
|
}).join(', DROP COLUMN');
|
||||||
|
|
||||||
const doBatch = (t) => {
|
const doBatch = (t) => {
|
||||||
const batch = [
|
const batch = [
|
||||||
|
|||||||
@@ -13,7 +13,6 @@
|
|||||||
// DatabaseController. This will let us replace the schema logic for
|
// DatabaseController. This will let us replace the schema logic for
|
||||||
// different databases.
|
// different databases.
|
||||||
// TODO: hide all schema logic inside the database adapter.
|
// TODO: hide all schema logic inside the database adapter.
|
||||||
|
|
||||||
const Parse = require('parse/node').Parse;
|
const Parse = require('parse/node').Parse;
|
||||||
|
|
||||||
const defaultColumns = Object.freeze({
|
const defaultColumns = Object.freeze({
|
||||||
@@ -465,18 +464,22 @@ export default class SchemaController {
|
|||||||
|
|
||||||
// Finally we have checked to make sure the request is valid and we can start deleting fields.
|
// Finally we have checked to make sure the request is valid and we can start deleting fields.
|
||||||
// Do all deletions first, then a single save to _SCHEMA collection to handle all additions.
|
// Do all deletions first, then a single save to _SCHEMA collection to handle all additions.
|
||||||
const deletePromises = [];
|
const deletedFields = [];
|
||||||
const insertedFields = [];
|
const insertedFields = [];
|
||||||
Object.keys(submittedFields).forEach(fieldName => {
|
Object.keys(submittedFields).forEach(fieldName => {
|
||||||
if (submittedFields[fieldName].__op === 'Delete') {
|
if (submittedFields[fieldName].__op === 'Delete') {
|
||||||
const promise = this.deleteField(fieldName, className, database);
|
deletedFields.push(fieldName);
|
||||||
deletePromises.push(promise);
|
|
||||||
} else {
|
} else {
|
||||||
insertedFields.push(fieldName);
|
insertedFields.push(fieldName);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return Promise.all(deletePromises) // Delete Everything
|
let deletePromise = Promise.resolve();
|
||||||
|
if (deletedFields.length > 0) {
|
||||||
|
deletePromise = this.deleteFields(deletedFields, className, database);
|
||||||
|
}
|
||||||
|
|
||||||
|
return deletePromise // Delete Everything
|
||||||
.then(() => this.reloadData({ clearCache: true })) // Reload our Schema, so we have all the new values
|
.then(() => this.reloadData({ clearCache: true })) // Reload our Schema, so we have all the new values
|
||||||
.then(() => {
|
.then(() => {
|
||||||
const promises = insertedFields.map(fieldName => {
|
const promises = insertedFields.map(fieldName => {
|
||||||
@@ -647,24 +650,32 @@ export default class SchemaController {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete a field, and remove that data from all objects. This is intended
|
// maintain compatibility
|
||||||
|
deleteField(fieldName, className, database) {
|
||||||
|
return this.deleteFields([fieldName], className, database);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete fields, and remove that data from all objects. This is intended
|
||||||
// to remove unused fields, if other writers are writing objects that include
|
// to remove unused fields, if other writers are writing objects that include
|
||||||
// this field, the field may reappear. Returns a Promise that resolves with
|
// this field, the field may reappear. Returns a Promise that resolves with
|
||||||
// no object on success, or rejects with { code, error } on failure.
|
// no object on success, or rejects with { code, error } on failure.
|
||||||
// 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 function would close over it or access it via member.
|
// a database adapter and this function would close over it or access it via member.
|
||||||
deleteField(fieldName, className, database) {
|
deleteFields(fieldNames, className, database) {
|
||||||
if (!classNameIsValid(className)) {
|
if (!classNameIsValid(className)) {
|
||||||
throw new Parse.Error(Parse.Error.INVALID_CLASS_NAME, invalidClassNameMessage(className));
|
throw new Parse.Error(Parse.Error.INVALID_CLASS_NAME, invalidClassNameMessage(className));
|
||||||
}
|
}
|
||||||
if (!fieldNameIsValid(fieldName)) {
|
|
||||||
throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, `invalid field name: ${fieldName}`);
|
fieldNames.forEach(fieldName => {
|
||||||
}
|
if (!fieldNameIsValid(fieldName)) {
|
||||||
//Don't allow deleting the default fields.
|
throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, `invalid field name: ${fieldName}`);
|
||||||
if (!fieldNameIsValidForClass(fieldName, className)) {
|
}
|
||||||
throw new Parse.Error(136, `field ${fieldName} cannot be changed`);
|
//Don't allow deleting the default fields.
|
||||||
}
|
if (!fieldNameIsValidForClass(fieldName, className)) {
|
||||||
|
throw new Parse.Error(136, `field ${fieldName} cannot be changed`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return this.getOneSchema(className, false, {clearCache: true})
|
return this.getOneSchema(className, false, {clearCache: true})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
@@ -675,15 +686,24 @@ export default class SchemaController {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then(schema => {
|
.then(schema => {
|
||||||
if (!schema.fields[fieldName]) {
|
fieldNames.forEach(fieldName => {
|
||||||
throw new Parse.Error(255, `Field ${fieldName} does not exist, cannot delete.`);
|
if (!schema.fields[fieldName]) {
|
||||||
}
|
throw new Parse.Error(255, `Field ${fieldName} does not exist, cannot delete.`);
|
||||||
if (schema.fields[fieldName].type == 'Relation') {
|
}
|
||||||
//For relations, drop the _Join table
|
});
|
||||||
return database.adapter.deleteFields(className, schema, [fieldName])
|
|
||||||
.then(() => database.adapter.deleteClass(`_Join:${fieldName}:${className}`));
|
const schemaFields = { ...schema.fields };
|
||||||
}
|
return database.adapter.deleteFields(className, schema, fieldNames)
|
||||||
return database.adapter.deleteFields(className, schema, [fieldName]);
|
.then(() => {
|
||||||
|
return Promise.all(fieldNames.map(fieldName => {
|
||||||
|
const field = schemaFields[fieldName];
|
||||||
|
if (field && field.type === 'Relation') {
|
||||||
|
//For relations, drop the _Join table
|
||||||
|
return database.adapter.deleteClass(`_Join:${fieldName}:${className}`);
|
||||||
|
}
|
||||||
|
return Promise.resolve();
|
||||||
|
}));
|
||||||
|
});
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
this._cache.clear();
|
this._cache.clear();
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user