feat: uiSchema remove api (#196)

* feat: recursiveRemoveIfNoChildren

* feat: remove with removeParents

* refactor: removeSchema server hook

* fix: server hook test

* feat: insertInner with remove parent

* feat: onSelfMove server hook

* feat: removeParentsIfNoChildren server hook method

* refactor: ui_schema_tree_path to uiSchemaTreePath

* feat: insertAdjacent api params
This commit is contained in:
ChengLei Shao 2022-02-18 12:29:03 +08:00 committed by GitHub
parent 6c381313cb
commit b9bbbc8516
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 602 additions and 91 deletions

View File

@ -111,8 +111,8 @@ describe('server hooks', () => {
field: 'title',
method: 'removeSchema',
params: {
breakComponent: 'Grid',
removeEmptyParents: true,
breakRemoveOn: { 'x-component': 'Grid' },
removeParentsIfNoChildren: true,
},
},
],
@ -137,8 +137,8 @@ describe('server hooks', () => {
field: 'intro',
method: 'removeSchema',
params: {
breakComponent: 'Grid',
removeEmptyParents: true,
breakRemoveOn: { 'x-component': 'Grid' },
removeParentsIfNoChildren: true,
},
},
],
@ -188,8 +188,8 @@ describe('server hooks', () => {
collection: 'posts',
method: 'removeSchema',
params: {
breakComponent: 'row',
removeEmptyParents: true,
breakRemoveOn: { 'x-component': 'row' },
removeParentsIfNoChildren: true,
},
},
],
@ -304,4 +304,91 @@ describe('server hooks', () => {
const role2Menus = await db.getRepository<BelongsToManyRepository>('roles.menuUiSchemas', 'role2').find();
expect(role2Menus.length).toEqual(0);
});
it('should remove parents on self move', async () => {
const schema = {
'x-uid': 'A',
name: 'A',
properties: {
B: {
'x-uid': 'B',
properties: {
C: {
'x-uid': 'C',
properties: {
D: {
'x-uid': 'D',
'x-server-hooks': [
{
type: 'onSelfMove',
method: 'removeParentsIfNoChildren',
},
],
},
},
},
},
},
E: {
'x-uid': 'E',
},
},
};
await uiSchemaRepository.insert(schema);
await uiSchemaRepository.insertAfterEnd('E', {
'x-uid': 'F',
name: 'F',
properties: {
G: {
'x-uid': 'G',
properties: {
D: {
'x-uid': 'D',
},
},
},
},
});
const A = await uiSchemaRepository.getJsonSchema('A');
expect(A).toEqual({
properties: {
E: {
'x-uid': 'E',
'x-async': false,
'x-index': 2,
},
F: {
properties: {
G: {
properties: {
D: {
'x-server-hooks': [
{
type: 'onSelfMove',
method: 'removeParentsIfNoChildren',
},
],
'x-uid': 'D',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'G',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'F',
'x-async': false,
'x-index': 3,
},
},
name: 'A',
'x-uid': 'A',
'x-async': false,
});
});
});

View File

@ -274,4 +274,68 @@ describe('server hooks', () => {
}),
).toBeDefined();
});
it('should call onSelfMove', async () => {
const schema = {
'x-uid': 'A',
name: 'A',
properties: {
B: {
'x-uid': 'B',
properties: {
C: {
'x-uid': 'C',
properties: {
D: {
'x-uid': 'D',
'x-server-hooks': [
{
type: 'onSelfMove',
method: 'testOnSelfMove',
},
],
},
},
},
},
},
E: {
'x-uid': 'E',
},
},
};
const serverHooks = uiSchemaPlugin.serverHooks;
const jestFn = jest.fn();
serverHooks.register('onSelfMove', 'testOnSelfMove', async ({ options }) => {
jestFn();
});
await uiSchemaRepository.insert(schema);
await uiSchemaRepository.insertAfterEnd(
'E',
{
'x-uid': 'F',
name: 'F',
properties: {
G: {
'x-uid': 'G',
properties: {
D: {
'x-uid': 'D',
},
},
},
},
},
{
removeParentsIfNoChildren: true,
},
);
expect(jestFn).toHaveBeenCalled();
});
});

View File

@ -35,7 +35,7 @@ describe('ui_schema repository', () => {
},
});
repository = db.getCollection('uiSchemas').repository as UiSchemaRepository;
treePathCollection = db.getCollection('ui_schema_tree_path');
treePathCollection = db.getCollection('uiSchemaTreePath');
});
it('should be registered', async () => {
@ -50,7 +50,7 @@ describe('ui_schema repository', () => {
};
const transaction = await db.sequelize.transaction();
await repository.insertSingleNode(singleNode, transaction);
await repository.insertSingleNode(singleNode, { transaction });
await transaction.commit();
// it should save in ui schema tables
@ -76,7 +76,7 @@ describe('ui_schema repository', () => {
const transaction = await db.sequelize.transaction();
await repository.insertSingleNode(singleNode, transaction);
await repository.insertSingleNode(singleNode, { transaction });
const child1: SchemaNode = {
name: 'child1',
@ -88,7 +88,7 @@ describe('ui_schema repository', () => {
},
};
await repository.insertSingleNode(child1, transaction);
await repository.insertSingleNode(child1, { transaction });
const child11: SchemaNode = {
name: 'child11',
@ -99,7 +99,7 @@ describe('ui_schema repository', () => {
type: 'test',
},
};
await repository.insertSingleNode(child11, transaction);
await repository.insertSingleNode(child11, { transaction });
await transaction.commit();
expect(
@ -726,4 +726,215 @@ describe('ui_schema repository', () => {
expect(newTree.properties.a1.title).toEqual('new a1 title');
});
});
it('should insertInner with removeParent', async () => {
const schema = {
'x-uid': 'A',
name: 'A',
properties: {
B: {
'x-uid': 'B',
properties: {
C: {
'x-uid': 'C',
properties: {
D: {
'x-uid': 'D',
},
},
},
},
},
E: {
'x-uid': 'E',
},
},
};
await repository.insert(schema);
await repository.insertAfterBegin(
'E',
{
'x-uid': 'F',
name: 'F',
properties: {
G: {
'x-uid': 'G',
properties: {
D: {
'x-uid': 'D',
},
},
},
},
},
{
removeParentsIfNoChildren: true,
},
);
const A = await repository.getJsonSchema('A');
expect(A).toEqual({
properties: {
E: {
properties: {
F: {
properties: {
G: {
properties: {
D: {
'x-uid': 'D',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'G',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'F',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'E',
'x-async': false,
'x-index': 2,
},
},
name: 'A',
'x-uid': 'A',
'x-async': false,
});
});
it('should insertBeside with removeParent', async () => {
const schema = {
'x-uid': 'A',
name: 'A',
properties: {
B: {
'x-uid': 'B',
properties: {
C: {
'x-uid': 'C',
properties: {
D: {
'x-uid': 'D',
},
},
},
},
},
E: {
'x-uid': 'E',
},
},
};
await repository.insert(schema);
await repository.insertAfterEnd(
'E',
{
'x-uid': 'F',
name: 'F',
properties: {
G: {
'x-uid': 'G',
properties: {
D: {
'x-uid': 'D',
},
},
},
},
},
{
removeParentsIfNoChildren: true,
},
);
const A = await repository.getJsonSchema('A');
expect(A).toEqual({
properties: {
E: {
'x-uid': 'E',
'x-async': false,
'x-index': 2,
},
F: {
properties: {
G: {
'x-uid': 'G',
'x-async': false,
'x-index': 1,
properties: {
D: {
'x-uid': 'D',
'x-async': false,
'x-index': 1,
},
},
},
},
'x-uid': 'F',
'x-async': false,
'x-index': 3,
},
},
name: 'A',
'x-uid': 'A',
'x-async': false,
});
});
it('should remove with breakOn', async () => {
const schema = {
'x-uid': 'A',
name: 'A',
properties: {
B: {
'x-uid': 'B',
properties: {
C: {
'x-uid': 'C',
properties: {
D: {
'x-uid': 'D',
},
},
},
},
},
E: {
'x-uid': 'E',
},
},
};
await repository.insert(schema);
await repository.remove('D', {
removeParentsIfNoChildren: true,
});
const A = await repository.getJsonSchema('A');
expect(A).toEqual({
properties: {
E: {
'x-uid': 'E',
'x-async': false,
'x-index': 2,
},
},
name: 'A',
'x-uid': 'A',
'x-async': false,
});
});
});

View File

@ -53,10 +53,13 @@ export const uiSchemaActions = {
},
async insertAdjacent(ctx: Context, next) {
const { resourceIndex, position, values } = ctx.action.params;
const { resourceIndex, position, values, removeParentsIfNoChildren, breakRemoveOn } = ctx.action.params;
const repository = getRepositoryFromCtx(ctx);
ctx.body = await repository.insertAdjacent(position, resourceIndex, values);
ctx.body = await repository.insertAdjacent(position, resourceIndex, values, {
removeParentsIfNoChildren,
breakRemoveOn,
});
await next();
},
@ -68,9 +71,12 @@ export const uiSchemaActions = {
function insertPositionActionBuilder(position: 'beforeBegin' | 'afterBegin' | 'beforeEnd' | 'afterEnd') {
return async function (ctx: Context, next) {
const { resourceIndex, values } = ctx.action.params;
const { resourceIndex, values, removeParentsIfNoChildren, breakRemoveOn } = ctx.action.params;
const repository = getRepositoryFromCtx(ctx);
ctx.body = await repository.insertAdjacent(position, resourceIndex, values);
ctx.body = await repository.insertAdjacent(position, resourceIndex, values, {
removeParentsIfNoChildren,
breakRemoveOn,
});
await next();
};
}

View File

@ -1,5 +1,5 @@
export default {
name: 'ui_schema_tree_path',
name: 'uiSchemaTreePath',
autoGenId: false,
timestamps: false,
fields: [

View File

@ -1,5 +1,11 @@
import { MagicAttributeModel } from '@nocobase/database';
import { HookType } from './server-hooks';
class UiSchemaModel extends MagicAttributeModel {}
class UiSchemaModel extends MagicAttributeModel {
getServerHooksByType(type: HookType) {
const hooks = this.get('x-server-hooks') || [];
return hooks.filter((hook) => hook.type === type);
}
}
export { UiSchemaModel };

View File

@ -9,10 +9,20 @@ interface GetJsonSchemaOptions {
transaction?: Transaction;
}
type BreakRemoveOnType = {
[key: string]: any;
};
export interface removeParentOptions {
removeParentsIfNoChildren?: boolean;
breakRemoveOn?: BreakRemoveOnType;
}
interface InsertAdjacentOptions extends removeParentOptions {}
const nodeKeys = ['properties', 'definitions', 'patternProperties', 'additionalProperties', 'items'];
export class UiSchemaRepository extends Repository {
get uiSchemasTableName() {
if (this.database.sequelize.getDialect() === 'postgres') {
return `"${this.model.tableName}"`;
@ -21,7 +31,7 @@ export class UiSchemaRepository extends Repository {
}
get uiSchemaTreePathTableName() {
const model = this.database.getCollection('ui_schema_tree_path').model;
const model = this.database.getCollection('uiSchemaTreePath').model;
if (this.database.sequelize.getDialect() === 'postgres') {
return `"${model.tableName}"`;
}
@ -94,12 +104,8 @@ export class UiSchemaRepository extends Repository {
NodeInfo.type as type, NodeInfo.async as async, ParentPath.ancestor as parent, ParentPath.sort as sort
FROM ${this.uiSchemaTreePathTableName} as TreePath
LEFT JOIN ${this.uiSchemasTableName} as SchemaTable ON SchemaTable.uid = TreePath.descendant
LEFT JOIN ${
this.uiSchemaTreePathTableName
} as NodeInfo ON NodeInfo.descendant = SchemaTable.uid and NodeInfo.descendant = NodeInfo.ancestor and NodeInfo.depth = 0
LEFT JOIN ${
this.uiSchemaTreePathTableName
} as ParentPath ON (ParentPath.descendant = SchemaTable.uid AND ParentPath.depth = 1)
LEFT JOIN ${this.uiSchemaTreePathTableName} as NodeInfo ON NodeInfo.descendant = SchemaTable.uid and NodeInfo.descendant = NodeInfo.ancestor and NodeInfo.depth = 0
LEFT JOIN ${this.uiSchemaTreePathTableName} as ParentPath ON (ParentPath.descendant = SchemaTable.uid AND ParentPath.depth = 1)
WHERE TreePath.ancestor = :ancestor AND (NodeInfo.async = false or TreePath.depth = 1)`;
const nodes = await db.sequelize.query(rawSql, {
@ -194,7 +200,7 @@ export class UiSchemaRepository extends Repository {
}
treeCollection() {
return this.database.getCollection('ui_schema_tree_path');
return this.database.getCollection('uiSchemaTreePath');
}
async patch(newSchema: any, options?) {
@ -256,10 +262,30 @@ export class UiSchemaRepository extends Repository {
);
}
protected async isSingleChild(uid, transaction) {
protected async childrenCount(uid, transaction) {
const db = this.database;
const parent = await db.getRepository('ui_schema_tree_path').findOne({
const countResult = await db.sequelize.query(
`SELECT COUNT(*) as count FROM ${this.uiSchemaTreePathTableName} where ancestor = :ancestor and depth = 1`,
{
replacements: {
ancestor: uid,
},
type: 'SELECT',
transaction,
},
);
return parseInt(countResult[0]['count']);
}
protected async isLeafNode(uid, transaction) {
const childrenCount = await this.childrenCount(uid, transaction);
return childrenCount === 0;
}
async findParentUid(uid, transaction?) {
const parent = await this.database.getRepository('uiSchemaTreePath').findOne({
filter: {
descendant: uid,
depth: 1,
@ -267,29 +293,38 @@ export class UiSchemaRepository extends Repository {
transaction,
});
return parent ? (parent.get('ancestor') as string) : null;
}
protected async findNodeSchemaWithParent(uid, transaction) {
const schema = await this.database.getRepository('uiSchemas').findOne({
filter: {
uid,
},
transaction,
});
return {
parentUid: await this.findParentUid(uid, transaction),
schema,
};
}
protected async isSingleChild(uid, transaction) {
const db = this.database;
const parent = await this.findParentUid(uid, transaction);
if (!parent) {
return null;
}
const countResult = await db.sequelize.query(
`SELECT COUNT(*) as count FROM ${
db.getCollection('ui_schema_tree_path').model.tableName
} where ancestor = :ancestor and depth = 1`,
{
replacements: {
ancestor: parent.get('ancestor'),
},
type: 'SELECT',
transaction,
},
);
const parentChildrenCount = countResult[0]['count'];
const parentChildrenCount = await this.childrenCount(parent, transaction);
if (parentChildrenCount == 1) {
const schema = await db.getRepository('uiSchemas').findOne({
filter: {
uid: parent.get('ancestor') as string,
uid: parent,
},
transaction,
});
@ -300,15 +335,13 @@ export class UiSchemaRepository extends Repository {
return null;
}
async removeEmptyParents(options: TransactionAble & { uid: string; breakComponent?: string }) {
const { transaction, uid, breakComponent } = options;
async removeEmptyParents(options: TransactionAble & { uid: string; breakRemoveOn?: BreakRemoveOnType }) {
const { transaction, uid, breakRemoveOn } = options;
const removeParent = async (nodeUid: string) => {
const parent = await this.isSingleChild(nodeUid, transaction);
const nodeComponentType = parent ? parent.get('x-component') : null;
if ((parent && !breakComponent) || (parent && breakComponent != nodeComponentType)) {
if (parent && !this.breakOnMatched(parent, breakRemoveOn)) {
await removeParent(parent.get('uid') as string);
} else {
await this.remove(nodeUid, { transaction });
@ -318,7 +351,49 @@ export class UiSchemaRepository extends Repository {
await removeParent(uid);
}
async remove(uid: string, options?: TransactionAble) {
private breakOnMatched(schemaInstance, breakRemoveOn: BreakRemoveOnType): boolean {
if (!breakRemoveOn) {
return false;
}
for (const key of Object.keys(breakRemoveOn)) {
const instanceValue = schemaInstance.get(key);
const breakRemoveOnValue = breakRemoveOn[key];
if (instanceValue !== breakRemoveOnValue) {
return false;
}
}
return true;
}
async recursivelyRemoveIfNoChildren(options: TransactionAble & { uid: string; breakRemoveOn?: BreakRemoveOnType }) {
const { uid, transaction, breakRemoveOn } = options;
const removeLeafNode = async (nodeUid: string) => {
const isLeafNode = await this.isLeafNode(nodeUid, transaction);
if (isLeafNode) {
const { parentUid, schema } = await this.findNodeSchemaWithParent(nodeUid, transaction);
if (this.breakOnMatched(schema, breakRemoveOn)) {
// break at here
return;
} else {
// remove current node
await this.remove(nodeUid, {
transaction,
});
// continue remove
await removeLeafNode(parentUid);
}
}
};
await removeLeafNode(uid);
}
async remove(uid: string, options?: TransactionAble & removeParentOptions) {
let handleTransaction: boolean = true;
let transaction;
@ -332,6 +407,14 @@ export class UiSchemaRepository extends Repository {
const treePathTable = this.uiSchemaTreePathTableName;
try {
if (options?.removeParentsIfNoChildren) {
await this.removeEmptyParents({ transaction, uid, breakRemoveOn: options.breakRemoveOn });
if (handleTransaction) {
await transaction.commit();
}
return;
}
await this.database.sequelize.query(
`DELETE FROM ${this.uiSchemasTableName} WHERE uid IN (
SELECT descendant FROM ${treePathTable} WHERE ancestor = :uid
@ -371,16 +454,13 @@ export class UiSchemaRepository extends Repository {
}
}
async insertBeside(targetUid: string, schema: any, side: 'before' | 'after') {
const targetParent = await this.treeCollection().repository.findOne({
filter: {
descendant: targetUid,
depth: 1,
},
});
async insertBeside(targetUid: string, schema: any, side: 'before' | 'after', options?: InsertAdjacentOptions) {
const targetParent = await this.findParentUid(targetUid);
const db = this.database;
const treeTable = this.uiSchemaTreePathTableName;
const typeQuery = await db.sequelize.query(`SELECT type from ${treeTable} WHERE ancestor = :uid AND depth = 0;`, {
type: 'SELECT',
replacements: {
@ -393,7 +473,7 @@ export class UiSchemaRepository extends Repository {
const rootNode = nodes[0];
rootNode.childOptions = {
parentUid: targetParent.get('ancestor') as string,
parentUid: targetParent,
type: typeQuery[0]['type'],
position: {
type: side,
@ -401,41 +481,47 @@ export class UiSchemaRepository extends Repository {
},
};
const insertedNodes = await this.insertNodes(nodes);
const insertedNodes = await this.insertNodes(nodes, options);
return await this.getJsonSchema(insertedNodes[0].get('uid'));
}
async insertInner(targetUid: string, schema: any, position: 'first' | 'last') {
async insertInner(targetUid: string, schema: any, position: 'first' | 'last', options?: InsertAdjacentOptions) {
const nodes = UiSchemaRepository.schemaToSingleNodes(schema);
const rootNode = nodes[0];
rootNode.childOptions = {
parentUid: targetUid,
type: lodash.get(schema, 'x-node-type', 'properties'),
position,
};
const insertedNodes = await this.insertNodes(nodes);
const insertedNodes = await this.insertNodes(nodes, options);
return await this.getJsonSchema(insertedNodes[0].get('uid'));
}
async insertAdjacent(position: 'beforeBegin' | 'afterBegin' | 'beforeEnd' | 'afterEnd', target: string, schema: any) {
return await this[`insert${lodash.upperFirst(position)}`](target, schema);
async insertAdjacent(
position: 'beforeBegin' | 'afterBegin' | 'beforeEnd' | 'afterEnd',
target: string,
schema: any,
options?: InsertAdjacentOptions,
) {
return await this[`insert${lodash.upperFirst(position)}`](target, schema, options);
}
async insertAfterBegin(targetUid: string, schema: any) {
return await this.insertInner(targetUid, schema, 'first');
async insertAfterBegin(targetUid: string, schema: any, options?: InsertAdjacentOptions) {
return await this.insertInner(targetUid, schema, 'first', options);
}
async insertBeforeEnd(targetUid: string, schema: any) {
return await this.insertInner(targetUid, schema, 'last');
async insertBeforeEnd(targetUid: string, schema: any, options?: InsertAdjacentOptions) {
return await this.insertInner(targetUid, schema, 'last', options);
}
async insertBeforeBegin(targetUid: string, schema: any) {
return await this.insertBeside(targetUid, schema, 'before');
async insertBeforeBegin(targetUid: string, schema: any, options?: InsertAdjacentOptions) {
return await this.insertBeside(targetUid, schema, 'before', options);
}
async insertAfterEnd(targetUid: string, schema: any) {
return await this.insertBeside(targetUid, schema, 'after');
async insertAfterEnd(targetUid: string, schema: any, options?: InsertAdjacentOptions) {
return await this.insertBeside(targetUid, schema, 'after', options);
}
async insertNodes(nodes: SchemaNode[], options?) {
@ -453,7 +539,12 @@ export class UiSchemaRepository extends Repository {
try {
for (const node of nodes) {
insertedNodes.push(await this.insertSingleNode(node, transaction));
insertedNodes.push(
await this.insertSingleNode(node, {
...options,
transaction,
}),
);
}
if (handleTransaction) {
@ -497,9 +588,11 @@ export class UiSchemaRepository extends Repository {
return node;
}
async insertSingleNode(schema: SchemaNode, transaction: Transaction) {
async insertSingleNode(schema: SchemaNode, options: TransactionAble & removeParentOptions) {
const { transaction } = options;
const db = this.database;
const treeCollection = db.getCollection('ui_schema_tree_path');
const treeCollection = db.getCollection('uiSchemaTreePath');
const uid = schema['x-uid'];
const name = schema['name'];
@ -530,6 +623,7 @@ export class UiSchemaRepository extends Repository {
}
if (childOptions) {
const oldParentUid = await this.findParentUid(uid, transaction);
const parentUid = childOptions.parentUid;
const isTreeQuery = await db.sequelize.query(
@ -547,6 +641,7 @@ export class UiSchemaRepository extends Repository {
// if node is a tree root move tree to new path
if (isTree) {
// delete old tree path
await db.sequelize.query(
`DELETE FROM ${treeTable}
WHERE descendant IN (SELECT descendant FROM (SELECT descendant FROM ${treeTable} WHERE ancestor = :uid) as descendantTable )
@ -561,6 +656,7 @@ export class UiSchemaRepository extends Repository {
},
);
// insert new tree path
await db.sequelize.query(
`INSERT INTO ${treeTable} (ancestor, descendant, depth)
SELECT supertree.ancestor, subtree.descendant, supertree.depth + subtree.depth + 1
@ -738,6 +834,23 @@ WHERE TreeTable.depth = 1 AND TreeTable.ancestor = :ancestor and TreeTable.sort
},
transaction,
});
// move node to new parent
if (oldParentUid !== null && oldParentUid !== parentUid) {
await this.database.emitAsync('uiSchemaMove', savedNode, {
transaction,
oldParentUid,
parentUid,
});
if (options.removeParentsIfNoChildren) {
await this.recursivelyRemoveIfNoChildren({
transaction,
uid: oldParentUid,
breakRemoveOn: options.breakRemoveOn,
});
}
}
} else {
// insert root node path
await db.sequelize.query(

View File

@ -1,11 +1,13 @@
import { hookFactory } from './factory';
import { removeSchema } from './remove-schema';
import { bindMenuToRole } from './bind-menu-to-row';
import { removeParentsIfNoChildren } from './remove-parents-if-no-children';
const hooks = [
hookFactory('onCollectionDestroy', 'removeSchema', removeSchema),
hookFactory('onCollectionFieldDestroy', 'removeSchema', removeSchema),
hookFactory('onSelfCreate', 'bindMenuToRole', bindMenuToRole),
hookFactory('onSelfMove', 'removeParentsIfNoChildren', removeParentsIfNoChildren),
];
export { hooks };

View File

@ -0,0 +1,11 @@
import { UiSchemaRepository } from '../../repository';
export async function removeParentsIfNoChildren({ schemaInstance, db, options, params }) {
const { transaction, oldParentUid } = options;
const uiSchemaRepository: UiSchemaRepository = db.getRepository('uiSchemas');
await uiSchemaRepository.recursivelyRemoveIfNoChildren({
transaction,
uid: oldParentUid,
breakRemoveOn: params?.breakRemoveOn,
});
}

View File

@ -5,10 +5,10 @@ export async function removeSchema({ schemaInstance, options, db, params }) {
const uiSchemaRepository: UiSchemaRepository = db.getRepository('uiSchemas');
const uid = schemaInstance.get('uid') as string;
if (params?.removeEmptyParents) {
if (params?.removeParentsIfNoChildren) {
await uiSchemaRepository.removeEmptyParents({
uid,
breakComponent: params['breakComponent'],
breakRemoveOn: params['breakRemoveOn'],
transaction,
});
} else {

View File

@ -7,7 +7,8 @@ export type HookType =
| 'onCollectionDestroy'
| 'onCollectionFieldDestroy'
| 'onAnyCollectionFieldDestroy'
| 'onSelfCreate';
| 'onSelfCreate'
| 'onSelfMove';
export class ServerHooks {
hooks = new Map<HookType, Map<string, any>>();
@ -34,6 +35,30 @@ export class ServerHooks {
this.db.on('uiSchemas.afterCreateWithAssociations', async (model, options) => {
await this.onUiSchemaCreate(model, options);
});
this.db.on('uiSchemaMove', async (model, options) => {
await this.onUiSchemaMove(model, options);
});
}
protected async callSchemaInstanceHooksByType(schemaInstance, options, type: HookType) {
const { transaction } = options;
const hooks = schemaInstance.getServerHooksByType(type);
for (const hook of hooks) {
const hookFunc = this.hooks.get(type)?.get(hook['method']);
await hookFunc({
schemaInstance,
options,
db: this.db,
params: hook['params'],
});
}
}
protected async onUiSchemaMove(schemaInstance, options) {
await this.callSchemaInstanceHooksByType(schemaInstance, options, 'onSelfMove');
}
protected async onCollectionDestroy(collectionModel, options) {
@ -89,21 +114,7 @@ export class ServerHooks {
}
protected async onUiSchemaCreate(schemaInstance, options) {
const { transaction } = options;
const serverHooks = schemaInstance.get('serverHooks') || [];
const onSelfCreateHooks = serverHooks.filter((serverHook) => serverHook.get('type') === 'onSelfCreate');
for (const serverHook of onSelfCreateHooks) {
const hookFunc = this.hooks.get('onSelfCreate')?.get(serverHook.get('method'));
await hookFunc({
schemaInstance,
options,
db: this.db,
params: serverHook.get('params'),
});
}
await this.callSchemaInstanceHooksByType(schemaInstance, options, 'onSelfCreate');
}
protected async findHooksAndCall(hooksFilter, hooksArgs, transaction) {