fix: reference options sync (#1061)
* fix: sync onDelete options between field and reverseField * feat: reference compatibility
This commit is contained in:
parent
8a8478cf4c
commit
83cc19a013
@ -10,22 +10,25 @@ class ReferencesMap {
|
|||||||
protected map: Map<string, Reference[]> = new Map();
|
protected map: Map<string, Reference[]> = new Map();
|
||||||
|
|
||||||
addReference(reference: Reference) {
|
addReference(reference: Reference) {
|
||||||
|
if (!reference.onDelete) {
|
||||||
|
reference.onDelete = 'SET NULL';
|
||||||
|
}
|
||||||
|
|
||||||
const existReference = this.existReference(reference);
|
const existReference = this.existReference(reference);
|
||||||
|
|
||||||
if (existReference) {
|
if (existReference && existReference.onDelete !== reference.onDelete) {
|
||||||
if (reference.onDelete && 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(
|
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(
|
||||||
reference,
|
reference,
|
||||||
)}`,
|
)}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!reference.onDelete) {
|
|
||||||
reference.onDelete = 'SET NULL';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.map.set(reference.targetCollectionName, [...(this.map.get(reference.targetCollectionName) || []), reference]);
|
this.map.set(reference.targetCollectionName, [...(this.map.get(reference.targetCollectionName) || []), reference]);
|
||||||
|
@ -106,6 +106,9 @@ export class ApplicationVersion {
|
|||||||
async get() {
|
async get() {
|
||||||
if (await this.app.db.collectionExistsInDb('applicationVersion')) {
|
if (await this.app.db.collectionExistsInDb('applicationVersion')) {
|
||||||
const model = await this.collection.model.findOne();
|
const model = await this.collection.model.findOne();
|
||||||
|
if (!model) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
return model.get('value') as any;
|
return model.get('value') as any;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
@ -56,6 +56,49 @@ describe('reverseField options', () => {
|
|||||||
expect(json.foreignKey).toBe(json.reverseField.foreignKey);
|
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 () => {
|
it('should update reverseField', async () => {
|
||||||
const field = await Field.repository.create({
|
const field = await Field.repository.create({
|
||||||
values: {
|
values: {
|
||||||
|
@ -81,9 +81,9 @@ export function afterCreateForForeignKeyField(db: Database) {
|
|||||||
await r.update({
|
await r.update({
|
||||||
filter: {
|
filter: {
|
||||||
collectionName,
|
collectionName,
|
||||||
options:{
|
options: {
|
||||||
primaryKey: true
|
primaryKey: true,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
values: {
|
values: {
|
||||||
sort: 0,
|
sort: 0,
|
||||||
|
@ -4,6 +4,7 @@ export function afterCreateForReverseField(db: Database) {
|
|||||||
return async (model, { transaction }) => {
|
return async (model, { transaction }) => {
|
||||||
const Field = db.getCollection('fields');
|
const Field = db.getCollection('fields');
|
||||||
const reverseKey = model.get('reverseKey');
|
const reverseKey = model.get('reverseKey');
|
||||||
|
|
||||||
if (!reverseKey) {
|
if (!reverseKey) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -4,19 +4,28 @@ export function beforeCreateForReverseField(db: Database) {
|
|||||||
return async (model, { transaction }) => {
|
return async (model, { transaction }) => {
|
||||||
const Field = db.getCollection('fields');
|
const Field = db.getCollection('fields');
|
||||||
const reverseKey = model.get('reverseKey');
|
const reverseKey = model.get('reverseKey');
|
||||||
|
|
||||||
if (!reverseKey) {
|
if (!reverseKey) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const reverse = await Field.model.findByPk(reverseKey, { transaction });
|
const reverse = await Field.model.findByPk(reverseKey, { transaction });
|
||||||
model.set('collectionName', reverse.get('target'));
|
model.set('collectionName', reverse.get('target'));
|
||||||
model.set('target', reverse.get('collectionName'));
|
model.set('target', reverse.get('collectionName'));
|
||||||
|
|
||||||
|
if (reverse.get('onDelete')) {
|
||||||
|
model.set('onDelete', reverse.get('onDelete'));
|
||||||
|
}
|
||||||
|
|
||||||
const reverseType = reverse.get('type') as any;
|
const reverseType = reverse.get('type') as any;
|
||||||
|
|
||||||
if (['hasMany', 'hasOne'].includes(reverseType)) {
|
if (['hasMany', 'hasOne'].includes(reverseType)) {
|
||||||
model.set('type', 'belongsTo');
|
model.set('type', 'belongsTo');
|
||||||
model.set('targetKey', reverse.get('sourceKey'));
|
model.set('targetKey', reverse.get('sourceKey'));
|
||||||
model.set('foreignKey', reverse.get('foreignKey'));
|
model.set('foreignKey', reverse.get('foreignKey'));
|
||||||
model.set('sourceKey', reverse.get('targetKey'));
|
model.set('sourceKey', reverse.get('targetKey'));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (['belongsTo'].includes(reverseType)) {
|
if (['belongsTo'].includes(reverseType)) {
|
||||||
if (!model.get('type')) {
|
if (!model.get('type')) {
|
||||||
model.set('type', 'hasMany');
|
model.set('type', 'hasMany');
|
||||||
@ -25,6 +34,7 @@ export function beforeCreateForReverseField(db: Database) {
|
|||||||
model.set('foreignKey', reverse.get('foreignKey'));
|
model.set('foreignKey', reverse.get('foreignKey'));
|
||||||
model.set('targetKey', reverse.get('sourceKey'));
|
model.set('targetKey', reverse.get('sourceKey'));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (['belongsToMany'].includes(reverseType)) {
|
if (['belongsToMany'].includes(reverseType)) {
|
||||||
model.set('type', 'belongsToMany');
|
model.set('type', 'belongsToMany');
|
||||||
model.set('through', reverse.get('through'));
|
model.set('through', reverse.get('through'));
|
||||||
|
@ -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 {
|
protected getFieldCollection(): Collection | null {
|
||||||
const collectionName = this.get('collectionName');
|
const collectionName = this.get('collectionName');
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ import {
|
|||||||
beforeCreateForChildrenCollection,
|
beforeCreateForChildrenCollection,
|
||||||
beforeCreateForReverseField,
|
beforeCreateForReverseField,
|
||||||
beforeDestroyForeignKey,
|
beforeDestroyForeignKey,
|
||||||
beforeInitOptions
|
beforeInitOptions,
|
||||||
} from './hooks';
|
} from './hooks';
|
||||||
|
|
||||||
import { CollectionModel, FieldModel } from './models';
|
import { CollectionModel, FieldModel } from './models';
|
||||||
@ -106,6 +106,13 @@ export class CollectionManagerPlugin extends Plugin {
|
|||||||
if (prevDefaultValue != currentDefaultValue) {
|
if (prevDefaultValue != currentDefaultValue) {
|
||||||
await model.syncDefaultValue({ transaction, defaultValue: 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 }) => {
|
this.app.db.on('fields.afterSaveWithAssociations', async (model: FieldModel, { context, transaction }) => {
|
||||||
|
Loading…
Reference in New Issue
Block a user