fix: reference check after remove collection (#1123)

This commit is contained in:
ChengLei Shao 2022-11-22 16:19:48 +08:00 committed by GitHub
parent 54e92918b2
commit 3556ddc730
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 91 additions and 4 deletions

View File

@ -260,7 +260,9 @@ export class Collection<
if (!this.fields.has(name)) { if (!this.fields.has(name)) {
return; return;
} }
const field = this.fields.get(name); const field = this.fields.get(name);
const bool = this.fields.delete(name); const bool = this.fields.delete(name);
if (bool) { if (bool) {
this.emit('field.afterRemove', field); this.emit('field.afterRemove', field);

View File

@ -356,6 +356,8 @@ export class Database extends EventEmitter implements AsyncEmitter {
const collection = this.collections.get(name); const collection = this.collections.get(name);
this.emit('beforeRemoveCollection', collection); this.emit('beforeRemoveCollection', collection);
collection.resetFields();
const result = this.collections.delete(name); const result = this.collections.delete(name);
this.sequelize.modelManager.removeModel(collection.model); this.sequelize.modelManager.removeModel(collection.model);

View File

@ -1,3 +1,5 @@
import lodash from 'lodash';
export interface Reference { export interface Reference {
sourceCollectionName: string; sourceCollectionName: string;
sourceField: string; sourceField: string;
@ -21,7 +23,7 @@ class ReferencesMap {
// using existing reference // using existing reference
return; return;
} else if (existReference.onDelete === 'SET NULL') { } else if (existReference.onDelete === 'SET NULL') {
this.removeReference(existReference); existReference.onDelete = reference.onDelete;
} else { } else {
throw new Error( throw new Error(
`On Delete Conflict, exist reference ${JSON.stringify(existReference)}, new reference ${JSON.stringify( `On Delete Conflict, exist reference ${JSON.stringify(existReference)}, new reference ${JSON.stringify(
@ -31,7 +33,12 @@ class ReferencesMap {
} }
} }
this.map.set(reference.targetCollectionName, [...(this.map.get(reference.targetCollectionName) || []), reference]); if (!existReference) {
this.map.set(reference.targetCollectionName, [
...(this.map.get(reference.targetCollectionName) || []),
reference,
]);
}
} }
getReferences(collectionName) { getReferences(collectionName) {
@ -55,7 +62,8 @@ class ReferencesMap {
if (!references) { if (!references) {
return; return;
} }
const keys = Object.keys(reference);
const keys = ['sourceCollectionName', 'sourceField', 'targetField', 'targetCollectionName'];
this.map.set( this.map.set(
reference.targetCollectionName, reference.targetCollectionName,

View File

@ -27,6 +27,7 @@ export async function referentialIntegrityCheck(options: ReferentialIntegrityChe
const filter = { const filter = {
[sourceField]: referencedInstance[targetField], [sourceField]: referencedInstance[targetField],
}; };
const referencingExists = await sourceRepository.count({ const referencingExists = await sourceRepository.count({
filter, filter,
transaction, transaction,

View File

@ -83,15 +83,19 @@ export class BelongsToField extends RelationField {
const tcoll = database.collections.get(this.target); const tcoll = database.collections.get(this.target);
const foreignKey = this.options.foreignKey; const foreignKey = this.options.foreignKey;
const field1 = collection.getField(foreignKey); const field1 = collection.getField(foreignKey);
const field2 = tcoll.findField((field) => { const field2 = tcoll.findField((field) => {
return field.type === 'hasMany' && field.foreignKey === foreignKey; return field.type === 'hasMany' && field.foreignKey === foreignKey;
}); });
if (!field1 && !field2) { if (!field1 && !field2) {
collection.model.removeAttribute(foreignKey); collection.model.removeAttribute(foreignKey);
} }
const association = collection.model.associations[this.name]; const association = collection.model.associations[this.name];
this.database.referenceMap.removeReference(this.reference(association)); const reference = this.reference(association);
this.database.referenceMap.removeReference(reference);
this.clearAccessors(); this.clearAccessors();
// 删掉 model 的关联字段 // 删掉 model 的关联字段

View File

@ -0,0 +1,70 @@
import Database, { Collection as DBCollection } from '@nocobase/database';
import { MockServer, mockServer } from '@nocobase/test';
describe('reference integrity check', () => {
let db: Database;
let app: MockServer;
beforeEach(async () => {
app = mockServer();
db = app.db;
});
afterEach(async () => {
await app.destroy();
});
it('should clean reference after collection destroy', async () => {
const users = db.collection({
name: 'users',
fields: [
{
type: 'string',
name: 'name',
},
{
type: 'hasOne',
name: 'profile',
onDelete: 'CASCADE',
},
],
});
const profiles = db.collection({
name: 'profiles',
fields: [
{
type: 'integer',
name: 'age',
},
{
type: 'belongsTo',
name: 'user',
onDelete: 'CASCADE',
},
],
});
await db.sync();
expect(db.referenceMap.getReferences('users').length).toEqual(1);
await users.repository.create({
values: {
name: 'foo',
profile: {
age: 18,
},
},
});
db.removeCollection('profiles');
expect(db.referenceMap.getReferences('users').length).toEqual(0);
await users.repository.destroy({
filter: {
name: 'foo',
},
});
});
});