fix: postgres sort with appends issue (#198)

* fix: postgres sort with appends issue

* fix: role menus api error

* feat: add exists params in roles.collections api

* refactor: remove uid field on uiSchemas table

* test: toggle role menus

* fix: plugin-acl test

* feat: sync data to acl before app start

* fix: mysql ui-schema sql compatibility

* feat: writeRolesToACL in plugin-acl
This commit is contained in:
ChengLei Shao 2022-02-20 01:23:04 +08:00 committed by GitHub
parent 7be30b5b95
commit 000587380b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 299 additions and 89 deletions

View File

@ -8,7 +8,7 @@ import {
} from './multiple-relation-repository'; } from './multiple-relation-repository';
import { CreateOptions, DestroyOptions, FindOptions, TK, TargetKey, UpdateOptions } from '../repository'; import { CreateOptions, DestroyOptions, FindOptions, TK, TargetKey, UpdateOptions } from '../repository';
import { transaction } from './relation-repository'; import { transaction } from './relation-repository';
import lodash from 'lodash'; import lodash, { omit } from 'lodash';
interface IHasManyRepository<M extends Model> { interface IHasManyRepository<M extends Model> {
find(options?: FindOptions): Promise<M>; find(options?: FindOptions): Promise<M>;
@ -29,6 +29,25 @@ interface IHasManyRepository<M extends Model> {
} }
export class HasManyRepository extends MultipleRelationRepository implements IHasManyRepository<any> { export class HasManyRepository extends MultipleRelationRepository implements IHasManyRepository<any> {
async find(options?: FindOptions): Promise<any> {
const targetRepository = this.targetCollection.repository;
const addFilter = {
[this.association.foreignKey]: this.sourceKeyValue,
};
if (options?.filterByTk) {
addFilter[this.associationField.targetKey] = options.filterByTk;
}
return await targetRepository.find({
...omit(options, ['filterByTk']),
filter: {
$and: [options.filter || {}, addFilter],
},
});
}
@transaction((args, transaction) => { @transaction((args, transaction) => {
return { return {
filterByTk: args[0], filterByTk: args[0],

View File

@ -44,7 +44,7 @@ export abstract class MultipleRelationRepository extends RelationRepository {
if (findOptions.include && findOptions.include.length > 0) { if (findOptions.include && findOptions.include.length > 0) {
const ids = ( const ids = (
await sourceModel[getAccessor]({ await sourceModel[getAccessor]({
...findOptions, ...omit(findOptions, 'order'),
includeIgnoreAttributes: false, includeIgnoreAttributes: false,
attributes: [this.targetKey()], attributes: [this.targetKey()],
group: `${this.targetModel.name}.${this.targetKey()}`, group: `${this.targetModel.name}.${this.targetKey()}`,

View File

@ -207,6 +207,7 @@ export class Repository<TModelAttributes extends {} = any, TCreationAttributes e
async find(options?: FindOptions) { async find(options?: FindOptions) {
const model = this.collection.model; const model = this.collection.model;
const transaction = await this.getTransaction(options); const transaction = await this.getTransaction(options);
const opts = { const opts = {
subQuery: false, subQuery: false,
...this.buildQueryOptions(options), ...this.buildQueryOptions(options),
@ -215,6 +216,7 @@ export class Repository<TModelAttributes extends {} = any, TCreationAttributes e
if (opts.include && opts.include.length > 0) { if (opts.include && opts.include.length > 0) {
// @ts-ignore // @ts-ignore
const primaryKeyField = model.primaryKeyField || model.primaryKeyAttribute; const primaryKeyField = model.primaryKeyField || model.primaryKeyAttribute;
const ids = ( const ids = (
await model.findAll({ await model.findAll({
...opts, ...opts,

View File

@ -1,13 +1,16 @@
import { ACL } from '@nocobase/acl'; import { ACL } from '@nocobase/acl';
import { Database } from '@nocobase/database'; import { Database } from '@nocobase/database';
import { MockServer } from '@nocobase/test'; import { MockServer } from '@nocobase/test';
import { prepareApp } from './prepare'; import { changeMockRole, prepareApp } from './prepare';
import { UiSchemaRepository } from '@nocobase/plugin-ui-schema-storage';
describe('acl', () => { describe('acl', () => {
let app: MockServer; let app: MockServer;
let db: Database; let db: Database;
let acl: ACL; let acl: ACL;
let uiSchemaRepository: UiSchemaRepository;
afterEach(async () => { afterEach(async () => {
await app.destroy(); await app.destroy();
}); });
@ -16,6 +19,8 @@ describe('acl', () => {
app = await prepareApp(); app = await prepareApp();
db = app.db; db = app.db;
acl = app.acl; acl = app.acl;
uiSchemaRepository = db.getRepository('uiSchemas');
}); });
it('should works with universal actions', async () => { it('should works with universal actions', async () => {
@ -68,20 +73,20 @@ describe('acl', () => {
}); });
it('should works with resources actions', async () => { it('should works with resources actions', async () => {
await db.getRepository('roles').create({ const role = await db.getRepository('roles').create({
values: { values: {
name: 'admin', name: 'admin',
title: 'Admin User', title: 'Admin User',
allowConfigure: true, allowConfigure: true,
strategy: {
actions: ['list'],
},
}, },
}); });
const role = await db.getRepository('roles').findOne({ changeMockRole('admin');
filter: {
name: 'admin',
},
});
// create c1 collection
await db.getRepository('collections').create({ await db.getRepository('collections').create({
values: { values: {
name: 'c1', name: 'c1',
@ -89,6 +94,7 @@ describe('acl', () => {
}, },
}); });
// create c2 collection
await db.getRepository('collections').create({ await db.getRepository('collections').create({
values: { values: {
name: 'c2', name: 'c2',
@ -96,6 +102,7 @@ describe('acl', () => {
}, },
}); });
// create c1 published scope
await app await app
.agent() .agent()
.resource('rolesResourcesScopes') .resource('rolesResourcesScopes')
@ -111,11 +118,11 @@ describe('acl', () => {
const publishedScope = await db.getRepository('rolesResourcesScopes').findOne(); const publishedScope = await db.getRepository('rolesResourcesScopes').findOne();
// set admin resources
await app await app
.agent() .agent()
.resource('roles.resources') .resource('roles.resources', 'admin')
.create({ .create({
associatedIndex: role.get('name') as string,
values: { values: {
name: 'c1', name: 'c1',
usingActionsConfig: true, usingActionsConfig: true,
@ -170,11 +177,11 @@ describe('acl', () => {
appends: ['actions'], appends: ['actions'],
}); });
expect(response.statusCode).toEqual(200);
const actions = response.body.data[0].actions; const actions = response.body.data[0].actions;
const collectionName = response.body.data[0].name; const collectionName = response.body.data[0].name;
const viewActionId = actions.find((action) => action.name === 'view').id;
await app await app
.agent() .agent()
.resource('roles.resources', role.get('name')) .resource('roles.resources', role.get('name'))
@ -185,7 +192,6 @@ describe('acl', () => {
usingActionsConfig: true, usingActionsConfig: true,
actions: [ actions: [
{ {
id: viewActionId,
name: 'view', name: 'view',
fields: ['title', 'age'], fields: ['title', 'age'],
}, },
@ -434,4 +440,94 @@ describe('acl', () => {
expect(newAllowFields.includes('description')).toBeTruthy(); expect(newAllowFields.includes('description')).toBeTruthy();
}); });
it('should get role menus', async () => {
const role = await db.getRepository('roles').create({
values: {
name: 'admin',
title: 'Admin User',
allowConfigure: true,
strategy: {
actions: ['view'],
},
},
});
changeMockRole('admin');
const menuResponse = await app.agent().resource('roles.menuUiSchemas', 'admin').list();
expect(menuResponse.statusCode).toEqual(200);
});
it('should toggle role menus', async () => {
const role = await db.getRepository('roles').create({
values: {
name: 'admin',
title: 'Admin User',
allowConfigure: true,
strategy: {
actions: ['*'],
},
},
});
changeMockRole('admin');
const schema = {
'x-uid': 'test',
};
await uiSchemaRepository.insert(schema);
const response = await app
.agent()
.resource('roles.menuUiSchemas', 'admin')
.toggle({
values: { tk: 'test' },
});
expect(response.statusCode).toEqual(200);
});
it('should sync data to acl before app start', async () => {
const role = await db.getRepository('roles').create({
values: {
name: 'admin',
title: 'Admin User',
allowConfigure: true,
resources: [
{
name: 'posts',
usingActionsConfig: true,
actions: [
{
name: 'view',
fields: ['title'],
},
],
},
],
},
hooks: false,
});
expect(acl.getRole('admin')).toBeUndefined();
await app.start();
expect(acl.getRole('admin')).toBeDefined();
expect(
acl.can({
role: 'admin',
resource: 'posts',
action: 'view',
}),
).toMatchObject({
role: 'admin',
resource: 'posts',
action: 'view',
});
});
}); });

View File

@ -52,9 +52,8 @@ describe('role resource api', () => {
// get collections list // get collections list
let response = await app let response = await app
.agent() .agent()
.resource('roles.collections') .resource('roles.collections', 'admin')
.list({ .list({
associatedIndex: role.get('name') as string,
sort: ['sort'], sort: ['sort'],
}); });
@ -65,20 +64,21 @@ describe('role resource api', () => {
name: 'c1', name: 'c1',
title: 'table1', title: 'table1',
usingConfig: 'strategy', usingConfig: 'strategy',
exists: false,
}, },
{ {
name: 'c2', name: 'c2',
title: 'table2', title: 'table2',
usingConfig: 'strategy', usingConfig: 'strategy',
exists: false,
}, },
]); ]);
// set resource actions // set resource actions
response = await app response = await app
.agent() .agent()
.resource('roles.resources') .resource('roles.resources', 'admin')
.create({ .create({
associatedIndex: role.get('name') as string,
values: { values: {
name: 'c1', name: 'c1',
usingActionsConfig: true, usingActionsConfig: true,

View File

@ -10,20 +10,28 @@ const roleCollectionsResource = {
const db: Database = ctx.db; const db: Database = ctx.db;
const collectionRepository = db.getRepository('collections'); const collectionRepository = db.getRepository('collections');
// all collections
const collections = await collectionRepository.find(); const collections = await collectionRepository.find();
// role collections
const roleResources = await db.getRepository('rolesResources').find({ const roleResources = await db.getRepository('rolesResources').find({
filter: { filter: {
roleName: role, roleName: role,
usingActionsConfig: true,
}, },
}); });
// role collections
const roleResourcesNames = roleResources.map((roleResource) => roleResource.get('name')); const roleResourcesNames = roleResources.map((roleResource) => roleResource.get('name'));
const roleResourceActionResourceNames = roleResources
.filter((roleResources) => roleResources.get('usingActionsConfig'))
.map((roleResources) => roleResources.get('name'));
ctx.body = collections ctx.body = collections
.map((collection) => { .map((collection) => {
const usingConfig: UsingConfigType = roleResourcesNames.includes(collection.get('name')) const exists = roleResourcesNames.includes(collection.get('name'));
const usingConfig: UsingConfigType = roleResourceActionResourceNames.includes(collection.get('name'))
? 'resourceAction' ? 'resourceAction'
: 'strategy'; : 'strategy';
@ -32,6 +40,7 @@ const roleCollectionsResource = {
title: collection.get('title') as string, title: collection.get('title') as string,
roleName: role, roleName: role,
usingConfig, usingConfig,
exists,
}; };
}) })
.sort((a, b) => (a.name > b.name ? 1 : -1)); .sort((a, b) => (a.name > b.name ? 1 : -1));

View File

@ -3,6 +3,7 @@ import { CollectionOptions } from '@nocobase/database';
export default { export default {
name: 'roles', name: 'roles',
autoGenId: false, autoGenId: false,
model: 'RoleModel',
fields: [ fields: [
{ {
type: 'uid', type: 'uid',
@ -44,7 +45,6 @@ export default {
type: 'belongsToMany', type: 'belongsToMany',
name: 'menuUiSchemas', name: 'menuUiSchemas',
target: 'uiSchemas', target: 'uiSchemas',
targetKey: 'uid',
}, },
{ {
type: 'hasMany', type: 'hasMany',

View File

@ -0,0 +1,21 @@
import { Model } from '@nocobase/database';
import { ACL } from '@nocobase/acl';
export class RoleModel extends Model {
writeToAcl(options: { acl: ACL }) {
const { acl } = options;
const roleName = this.get('name') as string;
let role = acl.getRole(roleName);
if (!role) {
role = acl.define({
role: roleName,
});
}
role.setStrategy({
...((this.get('strategy') as object) || {}),
allowConfigure: this.get('allowConfigure') as boolean,
});
}
}

View File

@ -4,6 +4,7 @@ import { availableActionResource } from './actions/available-actions';
import { roleCollectionsResource } from './actions/role-collections'; import { roleCollectionsResource } from './actions/role-collections';
import { RoleResourceActionModel } from './model/RoleResourceActionModel'; import { RoleResourceActionModel } from './model/RoleResourceActionModel';
import { RoleResourceModel } from './model/RoleResourceModel'; import { RoleResourceModel } from './model/RoleResourceModel';
import { RoleModel } from './model/RoleModel';
export interface AssociationFieldAction { export interface AssociationFieldAction {
associationActions: string[]; associationActions: string[];
@ -26,7 +27,6 @@ export class GrantHelper {
} }
export class PluginACL extends Plugin { export class PluginACL extends Plugin {
associationFieldsActions: AssociationFieldsActions = {}; associationFieldsActions: AssociationFieldsActions = {};
grantHelper = new GrantHelper(); grantHelper = new GrantHelper();
@ -100,11 +100,23 @@ export class PluginACL extends Plugin {
}); });
} }
async beforeLoad() { async writeRolesToACL() {
const roles = (await this.app.db.getRepository('roles').find({
appends: ['resources', 'resources.actions'],
})) as RoleModel[];
for (const role of roles) {
role.writeToAcl({ acl: this.acl });
for (const resource of role.get('resources') as RoleResourceModel[]) {
await this.writeResourceToACL(resource, null);
}
}
}
async beforeLoad() {
this.app.db.registerModels({ this.app.db.registerModels({
RoleResourceActionModel, RoleResourceActionModel,
RoleResourceModel, RoleResourceModel,
RoleModel,
}); });
this.registerAssociationFieldsActions(); this.registerAssociationFieldsActions();
@ -114,18 +126,9 @@ export class PluginACL extends Plugin {
this.app.db.on('roles.afterSave', async (model, options) => { this.app.db.on('roles.afterSave', async (model, options) => {
const { transaction } = options; const { transaction } = options;
const roleName = model.get('name');
let role = this.acl.getRole(roleName);
if (!role) { model.writeToAcl({
role = this.acl.define({ acl: this.acl,
role: model.get('name'),
});
}
role.setStrategy({
...(model.get('strategy') || {}),
allowConfigure: model.get('allowConfigure'),
}); });
// model is default // model is default
@ -229,6 +232,11 @@ export class PluginACL extends Plugin {
}); });
} }
}); });
// sync database role data to acl
this.app.on('beforeStart', async () => {
await this.writeRolesToACL();
});
} }
async load() { async load() {

View File

@ -0,0 +1,56 @@
import { Database } from '@nocobase/database';
import { mockServer, MockServer } from '@nocobase/test';
import CollectionManagerPlugin from '@nocobase/plugin-collection-manager';
import { UiSchemaStoragePlugin } from '@nocobase/plugin-ui-schema-storage';
describe('action test', () => {
let db: Database;
let app: MockServer;
beforeEach(async () => {
app = mockServer();
app.plugin(CollectionManagerPlugin);
app.plugin(UiSchemaStoragePlugin);
db = app.db;
await db.clean({ drop: true });
await app.loadAndInstall();
});
afterEach(async () => {
await app.destroy();
});
it('should append uiSchema', async () => {
await db.getRepository('collections').create({
values: {
name: 'posts',
},
});
await db.getRepository('fields').create({
values: {
name: 'title',
collectionName: 'posts',
type: 'string',
uiSchema: {
'x-uid': 'test',
},
},
});
// @ts-ignore
await db.getRepository('collections').load();
await db.sync();
const response = await app
.agent()
.resource('collections.fields', 'posts')
.list({
pageSize: 5,
appends: ['uiSchema'],
sort: ['sort'],
});
expect(response.statusCode).toEqual(200);
});
});

View File

@ -70,7 +70,7 @@ describe('ui schema model', () => {
properties: { properties: {
child1: { child1: {
title: 'child1', title: 'child1',
'x-uid': child1.get('uid'), 'x-uid': child1.get('x-uid'),
'x-async': false, 'x-async': false,
'x-index': 1, 'x-index': 1,
}, },
@ -129,7 +129,7 @@ describe('ui schema model', () => {
properties: { properties: {
child1: { child1: {
title: 'new-child1-title', title: 'new-child1-title',
'x-uid': child1.get('uid'), 'x-uid': child1.get('x-uid'),
'x-async': false, 'x-async': false,
'x-index': 1, 'x-index': 1,
}, },

View File

@ -56,7 +56,7 @@ describe('ui_schema repository', () => {
// it should save in ui schema tables // it should save in ui schema tables
const testNode = await repository.findOne({ const testNode = await repository.findOne({
filter: { filter: {
uid: 'test', 'x-uid': 'test',
}, },
}); });
@ -277,8 +277,8 @@ describe('ui_schema repository', () => {
( (
await treePathCollection.repository.findOne({ await treePathCollection.repository.findOne({
filter: { filter: {
ancestor: root.get('uid'), ancestor: root.get('x-uid'),
descendant: a1.get('uid'), descendant: a1.get('x-uid'),
depth: 1, depth: 1,
}, },
}) })
@ -289,8 +289,8 @@ describe('ui_schema repository', () => {
( (
await treePathCollection.repository.findOne({ await treePathCollection.repository.findOne({
filter: { filter: {
ancestor: root.get('uid'), ancestor: root.get('x-uid'),
descendant: b1.get('uid'), descendant: b1.get('x-uid'),
depth: 1, depth: 1,
}, },
}) })
@ -301,8 +301,8 @@ describe('ui_schema repository', () => {
( (
await treePathCollection.repository.findOne({ await treePathCollection.repository.findOne({
filter: { filter: {
ancestor: b1.get('uid'), ancestor: b1.get('x-uid'),
descendant: c1.get('uid'), descendant: c1.get('x-uid'),
depth: 1, depth: 1,
}, },
}) })
@ -313,8 +313,8 @@ describe('ui_schema repository', () => {
( (
await treePathCollection.repository.findOne({ await treePathCollection.repository.findOne({
filter: { filter: {
ancestor: b1.get('uid'), ancestor: b1.get('x-uid'),
descendant: d1.get('uid'), descendant: d1.get('x-uid'),
depth: 1, depth: 1,
}, },
}) })
@ -330,7 +330,7 @@ describe('ui_schema repository', () => {
}, },
}); });
const results = await repository.getJsonSchema(rootNode.get('uid') as string); const results = await repository.getJsonSchema(rootNode.get('x-uid') as string);
expect(results).toMatchObject({ expect(results).toMatchObject({
type: 'object', type: 'object',
@ -375,7 +375,7 @@ describe('ui_schema repository', () => {
}, },
}); });
const results = await repository.getProperties(rootNode.get('uid') as string); const results = await repository.getProperties(rootNode.get('x-uid') as string);
expect(results).toMatchObject({ expect(results).toMatchObject({
type: 'object', type: 'object',
@ -468,7 +468,7 @@ describe('ui_schema repository', () => {
}, },
}); });
rootUid = rootNode.get('uid') as string; rootUid = rootNode.get('x-uid') as string;
}); });
it('should insertAfterBegin', async () => { it('should insertAfterBegin', async () => {
@ -506,7 +506,7 @@ describe('ui_schema repository', () => {
}, },
}); });
await repository.insertBeforeBegin(b1Node.get('uid') as string, newNode); await repository.insertBeforeBegin(b1Node.get('x-uid') as string, newNode);
const schema = await repository.getJsonSchema(rootUid); const schema = await repository.getJsonSchema(rootUid);
expect(schema.properties['newNode']['x-index']).toEqual(2); expect(schema.properties['newNode']['x-index']).toEqual(2);
@ -525,7 +525,7 @@ describe('ui_schema repository', () => {
}, },
}); });
await repository.insertAfterEnd(a1Node.get('uid') as string, newNode); await repository.insertAfterEnd(a1Node.get('x-uid') as string, newNode);
const schema = await repository.getJsonSchema(rootUid); const schema = await repository.getJsonSchema(rootUid);
expect(schema.properties['newNode']['x-index']).toEqual(2); expect(schema.properties['newNode']['x-index']).toEqual(2);
@ -544,7 +544,7 @@ describe('ui_schema repository', () => {
}, },
}); });
await repository.insertAfterEnd(b1Node.get('uid') as string, newNode); await repository.insertAfterEnd(b1Node.get('x-uid') as string, newNode);
const schema = await repository.getJsonSchema(rootUid); const schema = await repository.getJsonSchema(rootUid);
expect(schema.properties['newNode']['x-index']).toEqual(3); expect(schema.properties['newNode']['x-index']).toEqual(3);
@ -705,7 +705,7 @@ describe('ui_schema repository', () => {
}, },
}); });
rootUid = rootNode.get('uid') as string; rootUid = rootNode.get('x-uid') as string;
}); });
it('should patch root ui schema', async () => { it('should patch root ui schema', async () => {
@ -924,6 +924,7 @@ describe('ui_schema repository', () => {
}); });
const A = await repository.getJsonSchema('A'); const A = await repository.getJsonSchema('A');
expect(A).toEqual({ expect(A).toEqual({
properties: { properties: {
E: { E: {

View File

@ -12,13 +12,6 @@ export default {
{ {
type: 'uid', type: 'uid',
name: 'x-uid', name: 'x-uid',
field: 'uid',
primaryKey: true,
},
{
type: 'string',
name: 'uid',
field: 'uid',
primaryKey: true, primaryKey: true,
}, },
{ {

View File

@ -38,6 +38,14 @@ export class UiSchemaRepository extends Repository {
return model.tableName; return model.tableName;
} }
sqlAdapter(sql: string) {
if (this.database.sequelize.getDialect() === 'mysql') {
return lodash.replace(sql, /"/g, '`');
}
return sql;
}
static schemaToSingleNodes(schema: any, carry: SchemaNode[] = [], childOptions: ChildOptions = null): SchemaNode[] { static schemaToSingleNodes(schema: any, carry: SchemaNode[] = [], childOptions: ChildOptions = null): SchemaNode[] {
const node = lodash.cloneDeep( const node = lodash.cloneDeep(
lodash.isString(schema) lodash.isString(schema)
@ -99,16 +107,16 @@ export class UiSchemaRepository extends Repository {
const db = this.database; const db = this.database;
const rawSql = ` const rawSql = `
SELECT SchemaTable.uid as uid, SchemaTable.name as name, SchemaTable.schema as "schema", SELECT "SchemaTable"."x-uid" as "x-uid", "SchemaTable"."name" as "name", "SchemaTable"."schema" as "schema",
TreePath.depth as depth, TreePath.depth as depth,
NodeInfo.type as type, NodeInfo.async as async, ParentPath.ancestor as parent, ParentPath.sort as sort NodeInfo.type as type, NodeInfo.async as async, ParentPath.ancestor as parent, ParentPath.sort as sort
FROM ${this.uiSchemaTreePathTableName} as TreePath FROM ${this.uiSchemaTreePathTableName} as TreePath
LEFT JOIN ${this.uiSchemasTableName} as SchemaTable ON SchemaTable.uid = TreePath.descendant LEFT JOIN ${this.uiSchemasTableName} as "SchemaTable" ON "SchemaTable"."x-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 NodeInfo ON NodeInfo.descendant = "SchemaTable"."x-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 ParentPath ON (ParentPath.descendant = "SchemaTable"."x-uid" AND ParentPath.depth = 1)
WHERE TreePath.ancestor = :ancestor AND (NodeInfo.async = false or TreePath.depth = 1)`; WHERE TreePath.ancestor = :ancestor AND (NodeInfo.async = false or TreePath.depth = 1)`;
const nodes = await db.sequelize.query(rawSql, { const nodes = await db.sequelize.query(this.sqlAdapter(rawSql), {
replacements: { replacements: {
ancestor: uid, ancestor: uid,
}, },
@ -128,17 +136,17 @@ export class UiSchemaRepository extends Repository {
const treeTable = this.uiSchemaTreePathTableName; const treeTable = this.uiSchemaTreePathTableName;
const rawSql = ` const rawSql = `
SELECT SchemaTable.uid as uid, SchemaTable.name as name, SchemaTable.schema as "schema" , SELECT "SchemaTable"."x-uid" as "x-uid", "SchemaTable"."name" as name, "SchemaTable"."schema" as "schema" ,
TreePath.depth as depth, TreePath.depth as depth,
NodeInfo.type as type, NodeInfo.async as async, ParentPath.ancestor as parent, ParentPath.sort as sort NodeInfo.type as type, NodeInfo.async as async, ParentPath.ancestor as parent, ParentPath.sort as sort
FROM ${treeTable} as TreePath FROM ${treeTable} as TreePath
LEFT JOIN ${this.uiSchemasTableName} as SchemaTable ON SchemaTable.uid = TreePath.descendant LEFT JOIN ${this.uiSchemasTableName} as "SchemaTable" ON "SchemaTable"."x-uid" = TreePath.descendant
LEFT JOIN ${treeTable} as NodeInfo ON NodeInfo.descendant = SchemaTable.uid and NodeInfo.descendant = NodeInfo.ancestor and NodeInfo.depth = 0 LEFT JOIN ${treeTable} as NodeInfo ON NodeInfo.descendant = "SchemaTable"."x-uid" and NodeInfo.descendant = NodeInfo.ancestor and NodeInfo.depth = 0
LEFT JOIN ${treeTable} as ParentPath ON (ParentPath.descendant = SchemaTable.uid AND ParentPath.depth = 1) LEFT JOIN ${treeTable} as ParentPath ON (ParentPath.descendant = "SchemaTable"."x-uid" AND ParentPath.depth = 1)
WHERE TreePath.ancestor = :ancestor ${options?.includeAsyncNode ? '' : 'AND (NodeInfo.async != true )'} WHERE TreePath.ancestor = :ancestor ${options?.includeAsyncNode ? '' : 'AND (NodeInfo.async != true )'}
`; `;
const nodes = await db.sequelize.query(rawSql, { const nodes = await db.sequelize.query(this.sqlAdapter(rawSql), {
replacements: { replacements: {
ancestor: uid, ancestor: uid,
}, },
@ -158,7 +166,7 @@ export class UiSchemaRepository extends Repository {
const schema = { const schema = {
...(lodash.isPlainObject(node.schema) ? node.schema : JSON.parse(node.schema)), ...(lodash.isPlainObject(node.schema) ? node.schema : JSON.parse(node.schema)),
...lodash.pick(node, [...nodeKeys, 'name']), ...lodash.pick(node, [...nodeKeys, 'name']),
['x-uid']: node.uid, ['x-uid']: node['x-uid'],
['x-async']: !!node.async, ['x-async']: !!node.async,
}; };
@ -170,7 +178,7 @@ export class UiSchemaRepository extends Repository {
}; };
const buildTree = (rootNode) => { const buildTree = (rootNode) => {
const children = nodes.filter((node) => node.parent == rootNode.uid); const children = nodes.filter((node) => node.parent == rootNode['x-uid']);
if (children.length > 0) { if (children.length > 0) {
const childrenGroupByType = lodash.groupBy(children, 'type'); const childrenGroupByType = lodash.groupBy(children, 'type');
@ -196,7 +204,7 @@ export class UiSchemaRepository extends Repository {
return nodeAttributeSanitize(rootNode); return nodeAttributeSanitize(rootNode);
}; };
return buildTree(nodes.find((node) => node.uid == rootUid)); return buildTree(nodes.find((node) => node['x-uid'] == rootUid));
} }
treeCollection() { treeCollection() {
@ -244,7 +252,7 @@ export class UiSchemaRepository extends Repository {
async updateNode(uid: string, schema: any, transaction?: Transaction) { async updateNode(uid: string, schema: any, transaction?: Transaction) {
const nodeModel = await this.findOne({ const nodeModel = await this.findOne({
filter: { filter: {
uid, 'x-uid': uid,
}, },
}); });
@ -299,7 +307,7 @@ export class UiSchemaRepository extends Repository {
protected async findNodeSchemaWithParent(uid, transaction) { protected async findNodeSchemaWithParent(uid, transaction) {
const schema = await this.database.getRepository('uiSchemas').findOne({ const schema = await this.database.getRepository('uiSchemas').findOne({
filter: { filter: {
uid, 'x-uid': uid,
}, },
transaction, transaction,
}); });
@ -324,7 +332,7 @@ export class UiSchemaRepository extends Repository {
if (parentChildrenCount == 1) { if (parentChildrenCount == 1) {
const schema = await db.getRepository('uiSchemas').findOne({ const schema = await db.getRepository('uiSchemas').findOne({
filter: { filter: {
uid: parent, 'x-uid': parent,
}, },
transaction, transaction,
}); });
@ -342,7 +350,7 @@ export class UiSchemaRepository extends Repository {
const parent = await this.isSingleChild(nodeUid, transaction); const parent = await this.isSingleChild(nodeUid, transaction);
if (parent && !this.breakOnMatched(parent, breakRemoveOn)) { if (parent && !this.breakOnMatched(parent, breakRemoveOn)) {
await removeParent(parent.get('uid') as string); await removeParent(parent.get('x-uid') as string);
} else { } else {
await this.remove(nodeUid, { transaction }); await this.remove(nodeUid, { transaction });
} }
@ -416,7 +424,7 @@ export class UiSchemaRepository extends Repository {
} }
await this.database.sequelize.query( await this.database.sequelize.query(
`DELETE FROM ${this.uiSchemasTableName} WHERE uid IN ( `DELETE FROM ${this.uiSchemasTableName} WHERE 'x-uid' IN (
SELECT descendant FROM ${treePathTable} WHERE ancestor = :uid SELECT descendant FROM ${treePathTable} WHERE ancestor = :uid
) )
`, `,
@ -482,7 +490,7 @@ export class UiSchemaRepository extends Repository {
}; };
const insertedNodes = await this.insertNodes(nodes, options); const insertedNodes = await this.insertNodes(nodes, options);
return await this.getJsonSchema(insertedNodes[0].get('uid')); return await this.getJsonSchema(insertedNodes[0].get('x-uid'));
} }
async insertInner(targetUid: string, schema: any, position: 'first' | 'last', options?: InsertAdjacentOptions) { async insertInner(targetUid: string, schema: any, position: 'first' | 'last', options?: InsertAdjacentOptions) {
@ -496,7 +504,7 @@ export class UiSchemaRepository extends Repository {
}; };
const insertedNodes = await this.insertNodes(nodes, options); const insertedNodes = await this.insertNodes(nodes, options);
return await this.getJsonSchema(insertedNodes[0].get('uid')); return await this.getJsonSchema(insertedNodes[0].get('x-uid'));
} }
async insertAdjacent( async insertAdjacent(
@ -563,7 +571,7 @@ export class UiSchemaRepository extends Repository {
async insert(schema: any, options?: TransactionAble) { async insert(schema: any, options?: TransactionAble) {
const nodes = UiSchemaRepository.schemaToSingleNodes(schema); const nodes = UiSchemaRepository.schemaToSingleNodes(schema);
const insertedNodes = await this.insertNodes(nodes, options); const insertedNodes = await this.insertNodes(nodes, options);
return this.getJsonSchema(insertedNodes[0].get('uid'), { return this.getJsonSchema(insertedNodes[0].get('x-uid'), {
transaction: options?.transaction, transaction: options?.transaction,
}); });
} }
@ -575,7 +583,6 @@ export class UiSchemaRepository extends Repository {
values: { values: {
name, name,
['x-uid']: uid, ['x-uid']: uid,
uid,
schema, schema,
serverHooks, serverHooks,
}, },
@ -592,7 +599,6 @@ export class UiSchemaRepository extends Repository {
const { transaction } = options; const { transaction } = options;
const db = this.database; const db = this.database;
const treeCollection = db.getCollection('uiSchemaTreePath');
const uid = schema['x-uid']; const uid = schema['x-uid'];
const name = schema['name']; const name = schema['name'];
@ -609,7 +615,7 @@ export class UiSchemaRepository extends Repository {
// check node exists or not // check node exists or not
const existsNode = await this.findOne({ const existsNode = await this.findOne({
filter: { filter: {
uid, 'x-uid': uid,
}, },
transaction, transaction,
}); });
@ -695,7 +701,7 @@ export class UiSchemaRepository extends Repository {
type: 'INSERT', type: 'INSERT',
transaction, transaction,
replacements: { replacements: {
modelKey: savedNode.get('uid'), modelKey: savedNode.get('x-uid'),
modelParentKey: parentUid, modelParentKey: parentUid,
}, },
}, },
@ -709,7 +715,7 @@ export class UiSchemaRepository extends Repository {
{ {
type: 'INSERT', type: 'INSERT',
replacements: { replacements: {
modelKey: savedNode.get('uid'), modelKey: savedNode.get('x-uid'),
type: childOptions.type, type: childOptions.type,
async, async,
}, },
@ -858,7 +864,7 @@ WHERE TreeTable.depth = 1 AND TreeTable.ancestor = :ancestor and TreeTable.sort
{ {
type: 'INSERT', type: 'INSERT',
replacements: { replacements: {
modelKey: savedNode.get('uid'), modelKey: savedNode.get('x-uid'),
async, async,
}, },
transaction, transaction,

View File

@ -8,7 +8,7 @@ export async function bindMenuToRole({ schemaInstance, db, options }) {
for (const role of addNewMenuRoles) { for (const role of addNewMenuRoles) {
await db.getRepository('roles.menuUiSchemas', role.get('name')).add({ await db.getRepository('roles.menuUiSchemas', role.get('name')).add({
tk: schemaInstance.get('uid'), tk: schemaInstance.get('x-uid'),
transaction, transaction,
}); });
} }

View File

@ -3,7 +3,7 @@ import { UiSchemaRepository } from '../../repository';
export async function removeSchema({ schemaInstance, options, db, params }) { export async function removeSchema({ schemaInstance, options, db, params }) {
const { transaction } = options; const { transaction } = options;
const uiSchemaRepository: UiSchemaRepository = db.getRepository('uiSchemas'); const uiSchemaRepository: UiSchemaRepository = db.getRepository('uiSchemas');
const uid = schemaInstance.get('uid') as string; const uid = schemaInstance.get('x-uid') as string;
if (params?.removeParentsIfNoChildren) { if (params?.removeParentsIfNoChildren) {
await uiSchemaRepository.removeEmptyParents({ await uiSchemaRepository.removeEmptyParents({

View File

@ -27,7 +27,6 @@ export class UiSchemaStoragePlugin extends Plugin {
this.registerRepository(); this.registerRepository();
db.on('uiSchemas.beforeCreate', function setUid(model) { db.on('uiSchemas.beforeCreate', function setUid(model) {
model.set('uid', model.get('x-uid'));
if (!model.get('name')) { if (!model.get('name')) {
model.set('name', uid()); model.set('name', uid());
} }