feat: ui schema cache (#877)
* feat(core/cache): support cache * perf(plugins/ui-schema-storage): cache schema * refactor(plugins/ui-schema-storage): handle schema cache in repository level, not in action * fix(plugins/ui-schema-storage): jsonSchema use s_ + x-uid and properties use p_ + x-uid cache prevent jsonSchema and properties cache override each other * test(plugins/ui-schema-storage): add ui_schema repository with cache test * build(create-nocobase-app): remove create-nocobase cli's cache-store-package option * test(plugins/ui-schema-storage): add ui_schema repository with cache test with readFromCache false * fix(plugins/ui-schema-storage): repository insertAdjacent and patch method clear cache fix Co-authored-by: chenos <chenlinxh@gmail.com>
This commit is contained in:
parent
1bdc29b706
commit
40554d8151
@ -0,0 +1,531 @@
|
|||||||
|
import { Database } from '@nocobase/database';
|
||||||
|
import { Cache } from '@nocobase/cache';
|
||||||
|
import { mockServer, MockServer } from '@nocobase/test';
|
||||||
|
import UiSchemaRepository, { GetJsonSchemaOptions, GetPropertiesOptions } from '../repository';
|
||||||
|
import PluginUiSchema from '../server';
|
||||||
|
|
||||||
|
describe('ui_schema repository with cache', () => {
|
||||||
|
let app: MockServer;
|
||||||
|
let db: Database;
|
||||||
|
let cache: Cache;
|
||||||
|
let repository: UiSchemaRepository;
|
||||||
|
let schema;
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
await app.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
app = mockServer({
|
||||||
|
registerActions: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
db = app.db;
|
||||||
|
cache = app.cache;
|
||||||
|
|
||||||
|
await db.sequelize.getQueryInterface().dropAllTables();
|
||||||
|
|
||||||
|
app.plugin(PluginUiSchema);
|
||||||
|
|
||||||
|
await app.load();
|
||||||
|
await db.sync({
|
||||||
|
force: false,
|
||||||
|
alter: {
|
||||||
|
drop: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
repository = db.getCollection('uiSchemas').repository as UiSchemaRepository;
|
||||||
|
repository.setCache(cache);
|
||||||
|
|
||||||
|
schema = {
|
||||||
|
type: 'object',
|
||||||
|
title: 'title',
|
||||||
|
name: 'root',
|
||||||
|
'x-uid': 'rootUid',
|
||||||
|
properties: {
|
||||||
|
a1: {
|
||||||
|
type: 'string',
|
||||||
|
title: 'A1',
|
||||||
|
'x-component': 'Input',
|
||||||
|
properties: {
|
||||||
|
c1: {
|
||||||
|
'x-uid': 'c1Uid',
|
||||||
|
type: 'string',
|
||||||
|
title: 'C1',
|
||||||
|
},
|
||||||
|
c2: {
|
||||||
|
'x-uid': 'c2Uid',
|
||||||
|
type: 'string',
|
||||||
|
title: 'C2',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
b1: {
|
||||||
|
'x-async': true, // 添加了一个异步节点
|
||||||
|
type: 'string',
|
||||||
|
title: 'B1',
|
||||||
|
properties: {
|
||||||
|
c1: {
|
||||||
|
type: 'string',
|
||||||
|
title: 'C1',
|
||||||
|
},
|
||||||
|
d1: {
|
||||||
|
'x-async': true,
|
||||||
|
type: 'string',
|
||||||
|
title: 'D1',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
it('repository init with cache', async () => {
|
||||||
|
expect(repository.cache).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should cache when getJsonSchema with readFromCache', async () => {
|
||||||
|
const xUid = schema['x-uid'];
|
||||||
|
// xUid not in cache
|
||||||
|
expect(await cache.get(xUid)).toBeUndefined();
|
||||||
|
await repository.insert(schema);
|
||||||
|
const result = await repository.getJsonSchema(xUid, { readFromCache: true } as GetJsonSchemaOptions);
|
||||||
|
|
||||||
|
// xUid's schema is cached
|
||||||
|
expect(await cache.get(`s_${xUid}`)).toMatchObject(result);
|
||||||
|
|
||||||
|
// delete from database
|
||||||
|
await repository.destroy({
|
||||||
|
filterByTk: xUid,
|
||||||
|
});
|
||||||
|
const databaseSchema = await repository.findById(xUid);
|
||||||
|
expect(databaseSchema).toBeNull();
|
||||||
|
|
||||||
|
// also getJsonSchema from cache
|
||||||
|
const secondResult = await repository.getJsonSchema(xUid, { readFromCache: true } as GetJsonSchemaOptions);
|
||||||
|
expect(secondResult).toMatchObject(result);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not cache when getJsonSchema with readFromCache false', async () => {
|
||||||
|
const xUid = schema['x-uid'];
|
||||||
|
// xUid not in cache
|
||||||
|
expect(await cache.get(xUid)).toBeUndefined();
|
||||||
|
await repository.insert(schema);
|
||||||
|
const result = await repository.getJsonSchema(xUid, { readFromCache: false } as GetJsonSchemaOptions);
|
||||||
|
expect(result).toBeDefined();
|
||||||
|
// xUid's schema is not cached
|
||||||
|
expect(await cache.get(`s_${xUid}`)).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should cache when getProperties with readFromCache', async () => {
|
||||||
|
const xUid = schema['x-uid'];
|
||||||
|
// xUid not in cache
|
||||||
|
expect(await cache.get(xUid)).toBeUndefined();
|
||||||
|
await repository.insert(schema);
|
||||||
|
const result = await repository.getProperties(xUid, { readFromCache: true } as GetPropertiesOptions);
|
||||||
|
|
||||||
|
// xUid's properties is cached
|
||||||
|
expect(await cache.get(`p_${xUid}`)).toMatchObject(result);
|
||||||
|
|
||||||
|
// delete from database
|
||||||
|
await repository.destroy({
|
||||||
|
filterByTk: xUid,
|
||||||
|
});
|
||||||
|
const databaseSchema = await repository.findById(xUid);
|
||||||
|
expect(databaseSchema).toBeNull();
|
||||||
|
|
||||||
|
// also getProperties from cache
|
||||||
|
const secondResult = await repository.getProperties(xUid, { readFromCache: true } as GetPropertiesOptions);
|
||||||
|
expect(secondResult).toMatchObject(result);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not cache when getProperties with readFromCache false', async () => {
|
||||||
|
const xUid = schema['x-uid'];
|
||||||
|
// xUid not in cache
|
||||||
|
expect(await cache.get(xUid)).toBeUndefined();
|
||||||
|
await repository.insert(schema);
|
||||||
|
const result = await repository.getProperties(xUid, { readFromCache: false } as GetPropertiesOptions);
|
||||||
|
|
||||||
|
expect(result).toBeDefined();
|
||||||
|
// xUid's properties is not cached
|
||||||
|
expect(await cache.get(`p_${xUid}`)).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should clear cache when remove', async () => {
|
||||||
|
const xUid = schema['x-uid'];
|
||||||
|
// xUid not in cache
|
||||||
|
expect(await cache.get(xUid)).toBeUndefined();
|
||||||
|
|
||||||
|
await repository.insert(schema);
|
||||||
|
|
||||||
|
const sResult = await repository.getJsonSchema(xUid, { readFromCache: true } as GetJsonSchemaOptions);
|
||||||
|
expect(await cache.get(`s_${xUid}`)).toMatchObject(sResult);
|
||||||
|
|
||||||
|
const pResult = await repository.getProperties(xUid, { readFromCache: true } as GetPropertiesOptions);
|
||||||
|
expect(await cache.get(`p_${xUid}`)).toMatchObject(pResult);
|
||||||
|
|
||||||
|
await repository.remove(xUid);
|
||||||
|
|
||||||
|
expect(await cache.get(`s_${xUid}`)).toBeUndefined();
|
||||||
|
expect(await cache.get(`p_${xUid}`)).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should clear cache when remove children schema', async () => {
|
||||||
|
const xUid = schema['x-uid'];
|
||||||
|
// xUid not in cache
|
||||||
|
expect(await cache.get(xUid)).toBeUndefined();
|
||||||
|
|
||||||
|
await repository.insert(schema);
|
||||||
|
|
||||||
|
let sResult = await repository.getJsonSchema(xUid, { readFromCache: true } as GetJsonSchemaOptions);
|
||||||
|
expect(await cache.get(`s_${xUid}`)).toMatchObject(sResult);
|
||||||
|
expect(sResult.properties.a1.properties.c1).toBeDefined();
|
||||||
|
|
||||||
|
let pResult = (await repository.getProperties(xUid, { readFromCache: true } as GetPropertiesOptions)) as any;
|
||||||
|
expect(await cache.get(`p_${xUid}`)).toMatchObject(pResult);
|
||||||
|
expect(pResult.properties.a1.properties.c1).toBeDefined();
|
||||||
|
|
||||||
|
await repository.remove(schema.properties.a1.properties.c1['x-uid']);
|
||||||
|
|
||||||
|
expect(await cache.get(`s_${xUid}`)).toBeUndefined();
|
||||||
|
expect(await cache.get(`p_${xUid}`)).toBeUndefined();
|
||||||
|
|
||||||
|
sResult = await repository.getJsonSchema(xUid, { readFromCache: true } as GetJsonSchemaOptions);
|
||||||
|
expect(await cache.get(`s_${xUid}`)).toMatchObject(sResult);
|
||||||
|
expect(sResult.properties.a1.properties.c1).toBeUndefined();
|
||||||
|
|
||||||
|
pResult = (await repository.getProperties(xUid, { readFromCache: true } as GetPropertiesOptions)) as any;
|
||||||
|
expect(await cache.get(`p_${xUid}`)).toMatchObject(pResult);
|
||||||
|
expect(pResult.properties.a1.properties.c1).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should clear cache when patch', async () => {
|
||||||
|
const xUid = schema['x-uid'];
|
||||||
|
// xUid not in cache
|
||||||
|
expect(await cache.get(`s_${xUid}`)).toBeUndefined();
|
||||||
|
expect(await cache.get(`p_${xUid}`)).toBeUndefined();
|
||||||
|
|
||||||
|
await repository.insert(schema);
|
||||||
|
|
||||||
|
let sResult = await repository.getJsonSchema(xUid, { readFromCache: true } as GetJsonSchemaOptions);
|
||||||
|
expect(await cache.get(`s_${xUid}`)).toMatchObject(sResult);
|
||||||
|
|
||||||
|
let pResult = (await repository.getProperties(xUid, { readFromCache: true } as GetPropertiesOptions)) as any;
|
||||||
|
expect(await cache.get(`p_${xUid}`)).toMatchObject(pResult);
|
||||||
|
|
||||||
|
expect(sResult.title).toEqual(schema.title);
|
||||||
|
expect(pResult.properties.a1.title).toEqual(schema.properties.a1.title);
|
||||||
|
|
||||||
|
const newSchema = {
|
||||||
|
'x-uid': xUid,
|
||||||
|
title: 'test-title',
|
||||||
|
properties: {
|
||||||
|
a1: {
|
||||||
|
type: 'string',
|
||||||
|
title: 'new a1 title',
|
||||||
|
'x-component': 'Input',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
await repository.patch(newSchema);
|
||||||
|
|
||||||
|
expect(await cache.get(`s_${xUid}`)).toBeUndefined();
|
||||||
|
expect(await cache.get(`p_${xUid}`)).toBeUndefined();
|
||||||
|
|
||||||
|
// patched result
|
||||||
|
sResult = await repository.getJsonSchema(xUid, { readFromCache: true } as GetJsonSchemaOptions);
|
||||||
|
expect(await cache.get(`s_${xUid}`)).toMatchObject(sResult);
|
||||||
|
|
||||||
|
pResult = (await repository.getProperties(xUid, { readFromCache: true } as GetPropertiesOptions)) as any;
|
||||||
|
expect(await cache.get(`p_${xUid}`)).toMatchObject(pResult);
|
||||||
|
|
||||||
|
expect(sResult.title).toEqual(newSchema.title);
|
||||||
|
expect(pResult.properties.a1.title).toEqual(newSchema.properties.a1.title);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should clear cache when patch children schema', async () => {
|
||||||
|
const xUid = schema['x-uid'];
|
||||||
|
// xUid not in cache
|
||||||
|
expect(await cache.get(`s_${xUid}`)).toBeUndefined();
|
||||||
|
expect(await cache.get(`p_${xUid}`)).toBeUndefined();
|
||||||
|
|
||||||
|
await repository.insert(schema);
|
||||||
|
|
||||||
|
let sResult = await repository.getJsonSchema(xUid, { readFromCache: true } as GetJsonSchemaOptions);
|
||||||
|
expect(await cache.get(`s_${xUid}`)).toMatchObject(sResult);
|
||||||
|
|
||||||
|
let pResult = (await repository.getProperties(xUid, { readFromCache: true } as GetPropertiesOptions)) as any;
|
||||||
|
expect(await cache.get(`p_${xUid}`)).toMatchObject(pResult);
|
||||||
|
|
||||||
|
expect(sResult.properties.a1.properties.c1.title).toEqual(schema.properties.a1.properties.c1.title);
|
||||||
|
expect(pResult.properties.a1.properties.c1.title).toEqual(schema.properties.a1.properties.c1.title);
|
||||||
|
|
||||||
|
const newSchema = {
|
||||||
|
'x-uid': 'c1Uid',
|
||||||
|
type: 'string',
|
||||||
|
title: 'C1-test',
|
||||||
|
};
|
||||||
|
await repository.patch(newSchema);
|
||||||
|
|
||||||
|
expect(await cache.get(`s_${xUid}`)).toBeUndefined();
|
||||||
|
expect(await cache.get(`p_${xUid}`)).toBeUndefined();
|
||||||
|
|
||||||
|
// patched result
|
||||||
|
sResult = await repository.getJsonSchema(xUid, { readFromCache: true } as GetJsonSchemaOptions);
|
||||||
|
expect(await cache.get(`s_${xUid}`)).toMatchObject(sResult);
|
||||||
|
|
||||||
|
pResult = (await repository.getProperties(xUid, { readFromCache: true } as GetPropertiesOptions)) as any;
|
||||||
|
expect(await cache.get(`p_${xUid}`)).toMatchObject(pResult);
|
||||||
|
|
||||||
|
expect(sResult.properties.a1.properties.c1.title).toEqual(newSchema.title);
|
||||||
|
expect(pResult.properties.a1.properties.c1.title).toEqual(newSchema.title);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should clear cache when clearAncestor', async () => {
|
||||||
|
const xUid = schema['x-uid'];
|
||||||
|
// xUid not in cache
|
||||||
|
expect(await cache.get(`s_${xUid}`)).toBeUndefined();
|
||||||
|
expect(await cache.get(`p_${xUid}`)).toBeUndefined();
|
||||||
|
|
||||||
|
await repository.insert(schema);
|
||||||
|
|
||||||
|
let sResult = await repository.getJsonSchema(xUid, { readFromCache: true } as GetJsonSchemaOptions);
|
||||||
|
expect(await cache.get(`s_${xUid}`)).toMatchObject(sResult);
|
||||||
|
|
||||||
|
let pResult = (await repository.getProperties(xUid, { readFromCache: true } as GetPropertiesOptions)) as any;
|
||||||
|
expect(await cache.get(`p_${xUid}`)).toMatchObject(pResult);
|
||||||
|
|
||||||
|
expect(sResult.title).toEqual(schema.title);
|
||||||
|
expect(pResult.properties.a1.title).toEqual(schema.properties.a1.title);
|
||||||
|
|
||||||
|
await repository.clearAncestor(sResult.properties.a1['x-uid']);
|
||||||
|
|
||||||
|
expect(await cache.get(`s_${xUid}`)).toBeUndefined();
|
||||||
|
expect(await cache.get(`p_${xUid}`)).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should clear cache when insertAdjacent', async () => {
|
||||||
|
const xUid = schema['x-uid'];
|
||||||
|
// xUid not in cache
|
||||||
|
expect(await cache.get(`s_${xUid}`)).toBeUndefined();
|
||||||
|
expect(await cache.get(`p_${xUid}`)).toBeUndefined();
|
||||||
|
|
||||||
|
await repository.insert(schema);
|
||||||
|
|
||||||
|
let sResult = await repository.getJsonSchema(xUid, { readFromCache: true } as GetJsonSchemaOptions);
|
||||||
|
expect(await cache.get(`s_${xUid}`)).toMatchObject(sResult);
|
||||||
|
|
||||||
|
let pResult = (await repository.getProperties(xUid, { readFromCache: true } as GetPropertiesOptions)) as any;
|
||||||
|
expect(await cache.get(`p_${xUid}`)).toMatchObject(pResult);
|
||||||
|
|
||||||
|
expect(sResult.title).toEqual(schema.title);
|
||||||
|
expect(pResult.properties.a1.title).toEqual(schema.properties.a1.title);
|
||||||
|
await repository.insertAdjacent('beforeBegin', sResult.properties.a1['x-uid'], {
|
||||||
|
name: 'a0',
|
||||||
|
type: 'string',
|
||||||
|
title: 'a0 title',
|
||||||
|
'x-component': 'Input',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(await cache.get(`s_${xUid}`)).toBeUndefined();
|
||||||
|
expect(await cache.get(`p_${xUid}`)).toBeUndefined();
|
||||||
|
|
||||||
|
sResult = await repository.getJsonSchema(xUid, { readFromCache: true } as GetJsonSchemaOptions);
|
||||||
|
expect(await cache.get(`s_${xUid}`)).toMatchObject(sResult);
|
||||||
|
|
||||||
|
pResult = (await repository.getProperties(xUid, { readFromCache: true } as GetPropertiesOptions)) as any;
|
||||||
|
expect(await cache.get(`p_${xUid}`)).toMatchObject(pResult);
|
||||||
|
|
||||||
|
expect(sResult.properties?.a0).toBeDefined();
|
||||||
|
expect(pResult.properties?.a0).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should clear cache when insertAdjacent children schema', async () => {
|
||||||
|
const xUid = schema['x-uid'];
|
||||||
|
// xUid not in cache
|
||||||
|
expect(await cache.get(`s_${xUid}`)).toBeUndefined();
|
||||||
|
expect(await cache.get(`p_${xUid}`)).toBeUndefined();
|
||||||
|
|
||||||
|
await repository.insert(schema);
|
||||||
|
|
||||||
|
let sResult = await repository.getJsonSchema(xUid, { readFromCache: true } as GetJsonSchemaOptions);
|
||||||
|
expect(await cache.get(`s_${xUid}`)).toMatchObject(sResult);
|
||||||
|
|
||||||
|
let pResult = (await repository.getProperties(xUid, { readFromCache: true } as GetPropertiesOptions)) as any;
|
||||||
|
expect(await cache.get(`p_${xUid}`)).toMatchObject(pResult);
|
||||||
|
|
||||||
|
expect(sResult.title).toEqual(schema.title);
|
||||||
|
expect(pResult.properties.a1.title).toEqual(schema.properties.a1.title);
|
||||||
|
const c0Schema = {
|
||||||
|
name: 'c0',
|
||||||
|
type: 'string',
|
||||||
|
title: 'c0 title',
|
||||||
|
'x-component': 'Input',
|
||||||
|
};
|
||||||
|
await repository.insertAdjacent('afterBegin', sResult.properties.a1['x-uid'], c0Schema);
|
||||||
|
|
||||||
|
expect(await cache.get(`s_${xUid}`)).toBeUndefined();
|
||||||
|
expect(await cache.get(`p_${xUid}`)).toBeUndefined();
|
||||||
|
|
||||||
|
sResult = await repository.getJsonSchema(xUid, { readFromCache: true } as GetJsonSchemaOptions);
|
||||||
|
expect(await cache.get(`s_${xUid}`)).toMatchObject(sResult);
|
||||||
|
|
||||||
|
pResult = (await repository.getProperties(xUid, { readFromCache: true } as GetPropertiesOptions)) as any;
|
||||||
|
expect(await cache.get(`p_${xUid}`)).toMatchObject(pResult);
|
||||||
|
|
||||||
|
expect(sResult.properties.a1.properties.c0.title).toEqual(c0Schema.title);
|
||||||
|
expect(pResult.properties.a1.properties.c0.title).toEqual(c0Schema.title);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should clear cache when move children schema 1', async () => {
|
||||||
|
const schema = {
|
||||||
|
name: 'A',
|
||||||
|
'x-uid': 'A',
|
||||||
|
title: 'title A',
|
||||||
|
properties: {
|
||||||
|
B: {
|
||||||
|
name: 'B',
|
||||||
|
'x-uid': 'B',
|
||||||
|
title: 'title B',
|
||||||
|
properties: {
|
||||||
|
C: {
|
||||||
|
name: 'C',
|
||||||
|
'x-uid': 'C',
|
||||||
|
title: 'title C',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
D: {
|
||||||
|
name: 'D',
|
||||||
|
'x-uid': 'D',
|
||||||
|
title: 'title D',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const xUid = 'A';
|
||||||
|
// xUid not in cache
|
||||||
|
expect(await cache.get(`s_${xUid}`)).toBeUndefined();
|
||||||
|
expect(await cache.get(`p_${xUid}`)).toBeUndefined();
|
||||||
|
|
||||||
|
await repository.insert(schema);
|
||||||
|
|
||||||
|
let sResult = await repository.getJsonSchema(xUid, { readFromCache: true } as GetJsonSchemaOptions);
|
||||||
|
expect(await cache.get(`s_${xUid}`)).toMatchObject(sResult);
|
||||||
|
|
||||||
|
let pResult = (await repository.getProperties(xUid, { readFromCache: true } as GetPropertiesOptions)) as any;
|
||||||
|
expect(await cache.get(`p_${xUid}`)).toMatchObject(pResult);
|
||||||
|
|
||||||
|
expect(sResult.properties.B.properties.C).toBeDefined();
|
||||||
|
expect(sResult.properties.D).toBeDefined();
|
||||||
|
expect(pResult.properties.B.properties.C).toBeDefined();
|
||||||
|
expect(pResult.properties.D).toBeDefined();
|
||||||
|
|
||||||
|
await repository.insertAdjacent('afterBegin', schema.properties.D['x-uid'], schema.properties.B.properties.C);
|
||||||
|
|
||||||
|
expect(await cache.get(`s_${xUid}`)).toBeUndefined();
|
||||||
|
expect(await cache.get(`p_${xUid}`)).toBeUndefined();
|
||||||
|
|
||||||
|
sResult = await repository.getJsonSchema(xUid, { readFromCache: true } as GetJsonSchemaOptions);
|
||||||
|
expect(await cache.get(`s_${xUid}`)).toMatchObject(sResult);
|
||||||
|
|
||||||
|
pResult = (await repository.getProperties(xUid, { readFromCache: true } as GetPropertiesOptions)) as any;
|
||||||
|
expect(await cache.get(`p_${xUid}`)).toMatchObject(pResult);
|
||||||
|
|
||||||
|
expect(sResult.properties.B.properties?.C).toBeUndefined();
|
||||||
|
expect(sResult.properties.D.properties?.C).toBeDefined();
|
||||||
|
expect(sResult.properties.D.properties?.C?.title).toEqual(schema.properties.B.properties.C.title);
|
||||||
|
|
||||||
|
expect(pResult.properties.B.properties?.C).toBeUndefined();
|
||||||
|
expect(pResult.properties.D.properties?.C).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should clear cache when move children schema 2', async () => {
|
||||||
|
const schema1 = {
|
||||||
|
name: 'A',
|
||||||
|
'x-uid': 'A',
|
||||||
|
title: 'title A',
|
||||||
|
properties: {
|
||||||
|
B: {
|
||||||
|
name: 'B',
|
||||||
|
'x-uid': 'B',
|
||||||
|
title: 'title B',
|
||||||
|
properties: {
|
||||||
|
C: {
|
||||||
|
name: 'C',
|
||||||
|
'x-uid': 'C',
|
||||||
|
title: 'title C',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const schema2 = {
|
||||||
|
name: 'D',
|
||||||
|
'x-uid': 'D',
|
||||||
|
title: 'title D',
|
||||||
|
properties: {
|
||||||
|
E: {
|
||||||
|
name: 'E',
|
||||||
|
'x-uid': 'E',
|
||||||
|
title: 'title E',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const xUid_A = 'A';
|
||||||
|
const xUid_D = 'D';
|
||||||
|
// xUid not in cache
|
||||||
|
expect(await cache.get(`s_${xUid_A}`)).toBeUndefined();
|
||||||
|
expect(await cache.get(`p_${xUid_A}`)).toBeUndefined();
|
||||||
|
expect(await cache.get(`s_${xUid_D}`)).toBeUndefined();
|
||||||
|
expect(await cache.get(`p_${xUid_D}`)).toBeUndefined();
|
||||||
|
|
||||||
|
await repository.insert(schema1);
|
||||||
|
await repository.insert(schema2);
|
||||||
|
|
||||||
|
let sResult1 = await repository.getJsonSchema(xUid_A, { readFromCache: true } as GetJsonSchemaOptions);
|
||||||
|
expect(await cache.get(`s_${xUid_A}`)).toMatchObject(sResult1);
|
||||||
|
|
||||||
|
let pResult1 = (await repository.getProperties(xUid_A, { readFromCache: true } as GetPropertiesOptions)) as any;
|
||||||
|
expect(await cache.get(`p_${xUid_A}`)).toMatchObject(pResult1);
|
||||||
|
|
||||||
|
let sResult2 = await repository.getJsonSchema(xUid_D, { readFromCache: true } as GetJsonSchemaOptions);
|
||||||
|
expect(await cache.get(`s_${xUid_D}`)).toMatchObject(sResult2);
|
||||||
|
|
||||||
|
let pResult2 = (await repository.getProperties(xUid_D, { readFromCache: true } as GetPropertiesOptions)) as any;
|
||||||
|
expect(await cache.get(`p_${xUid_D}`)).toMatchObject(pResult2);
|
||||||
|
|
||||||
|
expect(sResult1.properties.B.properties.C).toBeDefined();
|
||||||
|
expect(pResult1.properties.B.properties.C).toBeDefined();
|
||||||
|
|
||||||
|
expect(sResult2.properties.E).toBeDefined();
|
||||||
|
expect(sResult2.properties.E).toBeDefined();
|
||||||
|
expect(sResult2.properties.E?.properties?.C).toBeUndefined();
|
||||||
|
expect(pResult2.properties.E?.properties?.C).toBeUndefined();
|
||||||
|
|
||||||
|
await repository.insertAdjacent('afterBegin', schema2.properties.E['x-uid'], schema1.properties.B.properties.C);
|
||||||
|
|
||||||
|
expect(await cache.get(`s_${xUid_A}`)).toBeUndefined();
|
||||||
|
expect(await cache.get(`p_${xUid_A}`)).toBeUndefined();
|
||||||
|
expect(await cache.get(`s_${xUid_D}`)).toBeUndefined();
|
||||||
|
expect(await cache.get(`p_${xUid_D}`)).toBeUndefined();
|
||||||
|
|
||||||
|
sResult1 = await repository.getJsonSchema(xUid_A, { readFromCache: true } as GetJsonSchemaOptions);
|
||||||
|
expect(await cache.get(`s_${xUid_A}`)).toMatchObject(sResult1);
|
||||||
|
|
||||||
|
pResult1 = (await repository.getProperties(xUid_A, { readFromCache: true } as GetPropertiesOptions)) as any;
|
||||||
|
expect(await cache.get(`p_${xUid_A}`)).toMatchObject(pResult1);
|
||||||
|
|
||||||
|
sResult2 = await repository.getJsonSchema(xUid_D, { readFromCache: true } as GetJsonSchemaOptions);
|
||||||
|
expect(await cache.get(`s_${xUid_D}`)).toMatchObject(sResult2);
|
||||||
|
|
||||||
|
pResult2 = (await repository.getProperties(xUid_D, { readFromCache: true } as GetPropertiesOptions)) as any;
|
||||||
|
expect(await cache.get(`p_${xUid_D}`)).toMatchObject(pResult2);
|
||||||
|
|
||||||
|
expect(sResult1.properties.B?.properties?.C).toBeUndefined();
|
||||||
|
expect(pResult1.properties.B?.properties?.C).toBeUndefined();
|
||||||
|
|
||||||
|
expect(sResult2.properties.E.properties.C).toBeDefined();
|
||||||
|
expect(pResult2.properties.E.properties.C).toBeDefined();
|
||||||
|
expect(sResult2.properties.E.properties?.C?.title).toEqual(schema1.properties.B.properties.C.title);
|
||||||
|
});
|
||||||
|
});
|
@ -1,10 +1,12 @@
|
|||||||
import { Context } from '@nocobase/actions';
|
import { Context } from '@nocobase/actions';
|
||||||
import { ActionParams } from '@nocobase/resourcer';
|
import { ActionParams } from '@nocobase/resourcer';
|
||||||
import lodash from 'lodash';
|
import lodash from 'lodash';
|
||||||
import UiSchemaRepository from '../repository';
|
import UiSchemaRepository, { GetJsonSchemaOptions, GetPropertiesOptions } from '../repository';
|
||||||
|
|
||||||
const getRepositoryFromCtx = (ctx: Context) => {
|
const getRepositoryFromCtx = (ctx: Context) => {
|
||||||
return ctx.db.getCollection('uiSchemas').repository as UiSchemaRepository;
|
const repo = ctx.db.getCollection('uiSchemas').repository as UiSchemaRepository;
|
||||||
|
repo.setCache(ctx.cache);
|
||||||
|
return repo;
|
||||||
};
|
};
|
||||||
|
|
||||||
const callRepositoryMethod = (method, paramsKey: 'resourceIndex' | 'values', optionsBuilder?) => {
|
const callRepositoryMethod = (method, paramsKey: 'resourceIndex' | 'values', optionsBuilder?) => {
|
||||||
@ -33,12 +35,21 @@ function parseInsertAdjacentValues(values) {
|
|||||||
|
|
||||||
export const uiSchemaActions = {
|
export const uiSchemaActions = {
|
||||||
getJsonSchema: callRepositoryMethod('getJsonSchema', 'resourceIndex', (params: ActionParams) => {
|
getJsonSchema: callRepositoryMethod('getJsonSchema', 'resourceIndex', (params: ActionParams) => {
|
||||||
|
const includeAsyncNode = params?.includeAsyncNode;
|
||||||
return {
|
return {
|
||||||
includeAsyncNode: params?.includeAsyncNode,
|
readFromCache: !includeAsyncNode,
|
||||||
};
|
includeAsyncNode,
|
||||||
|
} as GetJsonSchemaOptions;
|
||||||
}),
|
}),
|
||||||
|
|
||||||
getProperties: callRepositoryMethod('getProperties', 'resourceIndex'),
|
getProperties: callRepositoryMethod(
|
||||||
|
'getProperties',
|
||||||
|
'resourceIndex',
|
||||||
|
() =>
|
||||||
|
({
|
||||||
|
readFromCache: true,
|
||||||
|
} as GetPropertiesOptions),
|
||||||
|
),
|
||||||
insert: callRepositoryMethod('insert', 'values'),
|
insert: callRepositoryMethod('insert', 'values'),
|
||||||
insertNewSchema: callRepositoryMethod('insertNewSchema', 'values'),
|
insertNewSchema: callRepositoryMethod('insertNewSchema', 'values'),
|
||||||
remove: callRepositoryMethod('remove', 'resourceIndex'),
|
remove: callRepositoryMethod('remove', 'resourceIndex'),
|
||||||
@ -78,7 +89,7 @@ export const uiSchemaActions = {
|
|||||||
},
|
},
|
||||||
transaction,
|
transaction,
|
||||||
});
|
});
|
||||||
await db.getRepository<UiSchemaRepository>('uiSchemas').clearAncestor(filterByTk, { transaction });
|
await getRepositoryFromCtx(ctx).clearAncestor(filterByTk, { transaction });
|
||||||
ctx.body = {
|
ctx.body = {
|
||||||
result: 'ok',
|
result: 'ok',
|
||||||
};
|
};
|
||||||
|
@ -1,11 +1,18 @@
|
|||||||
import { Repository, Transactionable } from '@nocobase/database';
|
import { Repository, Transactionable } from '@nocobase/database';
|
||||||
|
import { Cache } from '@nocobase/cache';
|
||||||
import { uid } from '@nocobase/utils';
|
import { uid } from '@nocobase/utils';
|
||||||
import lodash from 'lodash';
|
import lodash from 'lodash';
|
||||||
import { Transaction } from 'sequelize';
|
import { Transaction } from 'sequelize';
|
||||||
import { ChildOptions, SchemaNode, TargetPosition } from './dao/ui_schema_node_dao';
|
import { ChildOptions, SchemaNode, TargetPosition } from './dao/ui_schema_node_dao';
|
||||||
|
|
||||||
interface GetJsonSchemaOptions {
|
export interface GetJsonSchemaOptions {
|
||||||
includeAsyncNode?: boolean;
|
includeAsyncNode?: boolean;
|
||||||
|
readFromCache?: boolean;
|
||||||
|
transaction?: Transaction;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GetPropertiesOptions {
|
||||||
|
readFromCache?: boolean;
|
||||||
transaction?: Transaction;
|
transaction?: Transaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,6 +71,36 @@ function transaction(transactionAbleArgPosition?: number) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class UiSchemaRepository extends Repository {
|
export class UiSchemaRepository extends Repository {
|
||||||
|
cache: Cache;
|
||||||
|
|
||||||
|
// if you need to handle cache in repo method, so you must set cache first
|
||||||
|
setCache(cache: Cache) {
|
||||||
|
this.cache = cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* clear cache with xUid which in uiSchemaTreePath's Path
|
||||||
|
* @param {string} xUid
|
||||||
|
* @param {Transaction} transaction
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
async clearXUidPathCache(xUid: string, transaction: Transaction) {
|
||||||
|
if (!this.cache || !xUid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// find all xUid node's parent nodes
|
||||||
|
const uiSchemaNodes = await this.database.getRepository('uiSchemaTreePath').find({
|
||||||
|
filter: {
|
||||||
|
descendant: xUid,
|
||||||
|
},
|
||||||
|
transaction: transaction,
|
||||||
|
});
|
||||||
|
for (const uiSchemaNode of uiSchemaNodes) {
|
||||||
|
await this.cache.del(`p_${uiSchemaNode['ancestor']}`);
|
||||||
|
await this.cache.del(`s_${uiSchemaNode['ancestor']}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
tableNameAdapter(tableName) {
|
tableNameAdapter(tableName) {
|
||||||
if (this.database.sequelize.getDialect() === 'postgres') {
|
if (this.database.sequelize.getDialect() === 'postgres') {
|
||||||
return `"${tableName}"`;
|
return `"${tableName}"`;
|
||||||
@ -144,7 +181,16 @@ export class UiSchemaRepository extends Repository {
|
|||||||
return carry;
|
return carry;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getProperties(uid: string, options: Transactionable = {}) {
|
async getProperties(uid: string, options: GetPropertiesOptions = {}) {
|
||||||
|
if (options?.readFromCache && this.cache) {
|
||||||
|
return this.cache.wrap(`p_${uid}`, () => {
|
||||||
|
return this.doGetProperties(uid, options);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return this.doGetProperties(uid, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async doGetProperties(uid: string, options: GetPropertiesOptions = {}) {
|
||||||
const { transaction } = options;
|
const { transaction } = options;
|
||||||
|
|
||||||
const db = this.database;
|
const db = this.database;
|
||||||
@ -174,7 +220,7 @@ export class UiSchemaRepository extends Repository {
|
|||||||
return lodash.pick(schema, ['type', 'properties']);
|
return lodash.pick(schema, ['type', 'properties']);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getJsonSchema(uid: string, options?: GetJsonSchemaOptions): Promise<any> {
|
private async doGetJsonSchema(uid: string, options?: GetJsonSchemaOptions) {
|
||||||
const db = this.database;
|
const db = this.database;
|
||||||
|
|
||||||
const treeTable = this.uiSchemaTreePathTableName;
|
const treeTable = this.uiSchemaTreePathTableName;
|
||||||
@ -201,8 +247,16 @@ export class UiSchemaRepository extends Repository {
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
const schema = this.nodesToSchema(nodes[0], uid);
|
return this.nodesToSchema(nodes[0], uid);
|
||||||
return schema;
|
}
|
||||||
|
|
||||||
|
async getJsonSchema(uid: string, options?: GetJsonSchemaOptions): Promise<any> {
|
||||||
|
if (options?.readFromCache && this.cache) {
|
||||||
|
return this.cache.wrap(`s_${uid}`, () => {
|
||||||
|
return this.doGetJsonSchema(uid, options);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return this.doGetJsonSchema(uid, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ignoreSchemaProperties(schemaProperties) {
|
private ignoreSchemaProperties(schemaProperties) {
|
||||||
@ -257,6 +311,7 @@ export class UiSchemaRepository extends Repository {
|
|||||||
|
|
||||||
@transaction()
|
@transaction()
|
||||||
async clearAncestor(uid: string, options?: Transactionable) {
|
async clearAncestor(uid: string, options?: Transactionable) {
|
||||||
|
await this.clearXUidPathCache(uid, options?.transaction);
|
||||||
const db = this.database;
|
const db = this.database;
|
||||||
const treeTable = this.uiSchemaTreePathTableName;
|
const treeTable = this.uiSchemaTreePathTableName;
|
||||||
|
|
||||||
@ -283,6 +338,7 @@ export class UiSchemaRepository extends Repository {
|
|||||||
const { transaction } = options;
|
const { transaction } = options;
|
||||||
|
|
||||||
const rootUid = newSchema['x-uid'];
|
const rootUid = newSchema['x-uid'];
|
||||||
|
await this.clearXUidPathCache(rootUid, transaction);
|
||||||
const oldTree = await this.getJsonSchema(rootUid);
|
const oldTree = await this.getJsonSchema(rootUid);
|
||||||
|
|
||||||
const traverSchemaTree = async (schema, path = []) => {
|
const traverSchemaTree = async (schema, path = []) => {
|
||||||
@ -311,7 +367,7 @@ export class UiSchemaRepository extends Repository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateNode(uid: string, schema: any, transaction?: Transaction) {
|
protected async updateNode(uid: string, schema: any, transaction?: Transaction) {
|
||||||
const nodeModel = await this.findOne({
|
const nodeModel = await this.findOne({
|
||||||
filter: {
|
filter: {
|
||||||
'x-uid': uid,
|
'x-uid': uid,
|
||||||
@ -354,7 +410,7 @@ export class UiSchemaRepository extends Repository {
|
|||||||
return childrenCount === 0;
|
return childrenCount === 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
async findParentUid(uid, transaction?) {
|
protected async findParentUid(uid, transaction?) {
|
||||||
const parent = await this.database.getRepository('uiSchemaTreePath').findOne({
|
const parent = await this.database.getRepository('uiSchemaTreePath').findOne({
|
||||||
filter: {
|
filter: {
|
||||||
descendant: uid,
|
descendant: uid,
|
||||||
@ -408,6 +464,8 @@ export class UiSchemaRepository extends Repository {
|
|||||||
async removeEmptyParents(options: Transactionable & { uid: string; breakRemoveOn?: BreakRemoveOnType }) {
|
async removeEmptyParents(options: Transactionable & { uid: string; breakRemoveOn?: BreakRemoveOnType }) {
|
||||||
const { transaction, uid, breakRemoveOn } = options;
|
const { transaction, uid, breakRemoveOn } = options;
|
||||||
|
|
||||||
|
await this.clearXUidPathCache(uid, transaction);
|
||||||
|
|
||||||
const removeParent = async (nodeUid: string) => {
|
const removeParent = async (nodeUid: string) => {
|
||||||
const parent = await this.isSingleChild(nodeUid, transaction);
|
const parent = await this.isSingleChild(nodeUid, transaction);
|
||||||
|
|
||||||
@ -440,6 +498,8 @@ export class UiSchemaRepository extends Repository {
|
|||||||
async recursivelyRemoveIfNoChildren(options: Transactionable & { uid: string; breakRemoveOn?: BreakRemoveOnType }) {
|
async recursivelyRemoveIfNoChildren(options: Transactionable & { uid: string; breakRemoveOn?: BreakRemoveOnType }) {
|
||||||
const { uid, transaction, breakRemoveOn } = options;
|
const { uid, transaction, breakRemoveOn } = options;
|
||||||
|
|
||||||
|
await this.clearXUidPathCache(uid, transaction);
|
||||||
|
|
||||||
const removeLeafNode = async (nodeUid: string) => {
|
const removeLeafNode = async (nodeUid: string) => {
|
||||||
const isLeafNode = await this.isLeafNode(nodeUid, transaction);
|
const isLeafNode = await this.isLeafNode(nodeUid, transaction);
|
||||||
|
|
||||||
@ -467,6 +527,7 @@ export class UiSchemaRepository extends Repository {
|
|||||||
async remove(uid: string, options?: Transactionable & removeParentOptions) {
|
async remove(uid: string, options?: Transactionable & removeParentOptions) {
|
||||||
let { transaction } = options;
|
let { transaction } = options;
|
||||||
|
|
||||||
|
await this.clearXUidPathCache(uid, transaction);
|
||||||
if (options?.removeParentsIfNoChildren) {
|
if (options?.removeParentsIfNoChildren) {
|
||||||
await this.removeEmptyParents({ transaction, uid, breakRemoveOn: options.breakRemoveOn });
|
await this.removeEmptyParents({ transaction, uid, breakRemoveOn: options.breakRemoveOn });
|
||||||
return;
|
return;
|
||||||
@ -501,7 +562,12 @@ export class UiSchemaRepository extends Repository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@transaction()
|
@transaction()
|
||||||
async insertBeside(targetUid: string, schema: any, side: 'before' | 'after', options?: InsertAdjacentOptions) {
|
protected async insertBeside(
|
||||||
|
targetUid: string,
|
||||||
|
schema: any,
|
||||||
|
side: 'before' | 'after',
|
||||||
|
options?: InsertAdjacentOptions,
|
||||||
|
) {
|
||||||
const { transaction } = options;
|
const { transaction } = options;
|
||||||
const targetParent = await this.findParentUid(targetUid, transaction);
|
const targetParent = await this.findParentUid(targetUid, transaction);
|
||||||
|
|
||||||
@ -537,7 +603,12 @@ export class UiSchemaRepository extends Repository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@transaction()
|
@transaction()
|
||||||
async insertInner(targetUid: string, schema: any, position: 'first' | 'last', options?: InsertAdjacentOptions) {
|
protected async insertInner(
|
||||||
|
targetUid: string,
|
||||||
|
schema: any,
|
||||||
|
position: 'first' | 'last',
|
||||||
|
options?: InsertAdjacentOptions,
|
||||||
|
) {
|
||||||
const { transaction } = options;
|
const { transaction } = options;
|
||||||
|
|
||||||
const nodes = UiSchemaRepository.schemaToSingleNodes(schema);
|
const nodes = UiSchemaRepository.schemaToSingleNodes(schema);
|
||||||
@ -585,6 +656,9 @@ export class UiSchemaRepository extends Repository {
|
|||||||
) {
|
) {
|
||||||
const { transaction } = options;
|
const { transaction } = options;
|
||||||
|
|
||||||
|
// if schema is existed then clear origin path schema cache
|
||||||
|
await this.clearXUidPathCache(schema['x-uid'], transaction);
|
||||||
|
|
||||||
if (options.wrap) {
|
if (options.wrap) {
|
||||||
// insert wrap schema using insertNewSchema
|
// insert wrap schema using insertNewSchema
|
||||||
const wrapSchemaNodes = await this.insertNewSchema(options.wrap, {
|
const wrapSchemaNodes = await this.insertNewSchema(options.wrap, {
|
||||||
@ -615,7 +689,10 @@ export class UiSchemaRepository extends Repository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return await this[`insert${lodash.upperFirst(position)}`](target, schema, options);
|
const result = await this[`insert${lodash.upperFirst(position)}`](target, schema, options);
|
||||||
|
// clear target schema path cache
|
||||||
|
await this.clearXUidPathCache(result['x-uid'], transaction);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@transaction()
|
@transaction()
|
||||||
@ -639,7 +716,7 @@ export class UiSchemaRepository extends Repository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@transaction()
|
@transaction()
|
||||||
async insertNodes(nodes: SchemaNode[], options?: Transactionable) {
|
protected async insertNodes(nodes: SchemaNode[], options?: Transactionable) {
|
||||||
const { transaction } = options;
|
const { transaction } = options;
|
||||||
|
|
||||||
const insertedNodes = [];
|
const insertedNodes = [];
|
||||||
@ -660,9 +737,11 @@ 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('x-uid'), {
|
const result = await this.getJsonSchema(insertedNodes[0].get('x-uid'), {
|
||||||
transaction: options?.transaction,
|
transaction: options?.transaction,
|
||||||
});
|
});
|
||||||
|
await this.clearXUidPathCache(result['x-uid'], options?.transaction);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@transaction()
|
@transaction()
|
||||||
@ -730,9 +809,11 @@ export class UiSchemaRepository extends Repository {
|
|||||||
return nodes;
|
return nodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.getJsonSchema(nodes[0]['x-uid'], {
|
const result = await this.getJsonSchema(nodes[0]['x-uid'], {
|
||||||
transaction,
|
transaction,
|
||||||
});
|
});
|
||||||
|
await this.clearXUidPathCache(result['x-uid'], transaction);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async insertSchemaRecord(name, uid, schema, transaction) {
|
private async insertSchemaRecord(name, uid, schema, transaction) {
|
||||||
@ -1026,7 +1107,7 @@ WHERE TreeTable.depth = 1 AND TreeTable.ancestor = :ancestor and TreeTable.sort
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
await this.clearXUidPathCache(uid, transaction);
|
||||||
return savedNode;
|
return savedNode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user