fix: reference options sync (#1061)

* fix: sync onDelete options between field and reverseField

* feat: reference compatibility
This commit is contained in:
ChengLei Shao 2022-11-10 21:28:33 +08:00 committed by GitHub
parent 8a8478cf4c
commit 83cc19a013
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 93 additions and 12 deletions

View File

@ -10,22 +10,25 @@ class ReferencesMap {
protected map: Map<string, Reference[]> = new Map();
addReference(reference: Reference) {
if (!reference.onDelete) {
reference.onDelete = 'SET NULL';
}
const existReference = this.existReference(reference);
if (existReference) {
if (reference.onDelete && existReference.onDelete !== reference.onDelete) {
if (existReference && existReference.onDelete !== reference.onDelete) {
if (reference.onDelete === 'SET NULL') {
// using existing reference
return;
} else if (existReference.onDelete === 'SET NULL') {
this.removeReference(existReference);
} else {
throw new Error(
`On Delete Conflict, exist reference ${JSON.stringify(existReference)}, new reference ${JSON.stringify(
reference,
)}`,
);
}
return;
}
if (!reference.onDelete) {
reference.onDelete = 'SET NULL';
}
this.map.set(reference.targetCollectionName, [...(this.map.get(reference.targetCollectionName) || []), reference]);

View File

@ -106,6 +106,9 @@ export class ApplicationVersion {
async get() {
if (await this.app.db.collectionExistsInDb('applicationVersion')) {
const model = await this.collection.model.findOne();
if (!model) {
return null;
}
return model.get('value') as any;
}
return null;

View File

@ -56,6 +56,49 @@ describe('reverseField options', () => {
expect(json.foreignKey).toBe(json.reverseField.foreignKey);
});
it('should sync onDelete options for reverse field', async () => {
const field = await Field.repository.create({
values: {
type: 'hasMany',
collectionName: 'tests',
target: 'targets',
onDelete: 'CASCADE',
reverseField: {},
},
});
const { reverseField } = field.toJSON();
expect(reverseField.onDelete).toBe('CASCADE');
});
it('should update reverseField onDelete options', async () => {
const field = await Field.repository.create({
values: {
type: 'hasMany',
collectionName: 'tests',
target: 'targets',
onDelete: 'CASCADE',
reverseField: {},
},
});
const { reverseField } = field.toJSON();
await Field.repository.update({
filterByTk: reverseField.key,
values: {
onDelete: 'SET NULL',
},
});
const mainField = await Field.repository.findOne({
filterByTk: field.get('key'),
});
expect(mainField.get('onDelete')).toBe('SET NULL');
});
it('should update reverseField', async () => {
const field = await Field.repository.create({
values: {

View File

@ -82,8 +82,8 @@ export function afterCreateForForeignKeyField(db: Database) {
filter: {
collectionName,
options: {
primaryKey: true
}
primaryKey: true,
},
},
values: {
sort: 0,

View File

@ -4,6 +4,7 @@ export function afterCreateForReverseField(db: Database) {
return async (model, { transaction }) => {
const Field = db.getCollection('fields');
const reverseKey = model.get('reverseKey');
if (!reverseKey) {
return;
}

View File

@ -4,19 +4,28 @@ export function beforeCreateForReverseField(db: Database) {
return async (model, { transaction }) => {
const Field = db.getCollection('fields');
const reverseKey = model.get('reverseKey');
if (!reverseKey) {
return;
}
const reverse = await Field.model.findByPk(reverseKey, { transaction });
model.set('collectionName', reverse.get('target'));
model.set('target', reverse.get('collectionName'));
if (reverse.get('onDelete')) {
model.set('onDelete', reverse.get('onDelete'));
}
const reverseType = reverse.get('type') as any;
if (['hasMany', 'hasOne'].includes(reverseType)) {
model.set('type', 'belongsTo');
model.set('targetKey', reverse.get('sourceKey'));
model.set('foreignKey', reverse.get('foreignKey'));
model.set('sourceKey', reverse.get('targetKey'));
}
if (['belongsTo'].includes(reverseType)) {
if (!model.get('type')) {
model.set('type', 'hasMany');
@ -25,6 +34,7 @@ export function beforeCreateForReverseField(db: Database) {
model.set('foreignKey', reverse.get('foreignKey'));
model.set('targetKey', reverse.get('sourceKey'));
}
if (['belongsToMany'].includes(reverseType)) {
model.set('type', 'belongsToMany');
model.set('through', reverse.get('through'));

View File

@ -156,6 +156,20 @@ export class FieldModel extends MagicAttributeModel {
);
}
async syncReferenceCheckOption(options: Transactionable) {
const reverseKey = this.get('reverseKey');
if (!reverseKey) return;
const reverseField = await this.db.getCollection('fields').repository.findOne({
filterByTk: reverseKey,
transaction: options.transaction,
});
if (!reverseField) return;
reverseField.set('onDelete', this.get('onDelete'));
await reverseField.save({ hooks: false, transaction: options.transaction });
}
protected getFieldCollection(): Collection | null {
const collectionName = this.get('collectionName');

View File

@ -13,7 +13,7 @@ import {
beforeCreateForChildrenCollection,
beforeCreateForReverseField,
beforeDestroyForeignKey,
beforeInitOptions
beforeInitOptions,
} from './hooks';
import { CollectionModel, FieldModel } from './models';
@ -106,6 +106,13 @@ export class CollectionManagerPlugin extends Plugin {
if (prevDefaultValue != currentDefaultValue) {
await model.syncDefaultValue({ transaction, defaultValue: currentDefaultValue });
}
const prevOnDelete = prevOptions['onDelete'];
const currentOnDelete = currentOptions['onDelete'];
if (prevOnDelete != currentOnDelete) {
await model.syncReferenceCheckOption({ transaction });
}
});
this.app.db.on('fields.afterSaveWithAssociations', async (model: FieldModel, { context, transaction }) => {