fix(plugin-field-sort): fix field registering for external data source (#6212)

This commit is contained in:
Junyi 2025-02-13 22:29:58 +08:00 committed by GitHub
parent dd7c7f641c
commit 7b715c847d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 338 additions and 292 deletions

View File

@ -7,329 +7,375 @@
* For more information, please refer to: https://www.nocobase.com/agreement.
*/
import { Database } from '@nocobase/database';
import { Database, mockDatabase } from '@nocobase/database';
import { createMockServer, MockServer } from '@nocobase/test';
import Plugin from '..';
import { SequelizeCollectionManager, SequelizeDataSource } from '@nocobase/data-source-manager';
import { uid } from '@nocobase/utils';
describe('sort field', () => {
let app: MockServer;
let db: Database;
let another: SequelizeDataSource;
let anotherDB: Database;
beforeEach(async () => {
app = await createMockServer({
plugins: [Plugin, 'data-source-main', 'error-handler'],
});
db = app.db;
await app.dataSourceManager.add(
new SequelizeDataSource({
name: 'another',
collectionManager: {
database: mockDatabase({
tablePrefix: `t${uid(5)}`,
}),
},
resourceManager: {},
}),
);
another = app.dataSourceManager.dataSources.get('another') as SequelizeDataSource;
anotherDB = (another.collectionManager as SequelizeCollectionManager).db;
another.acl.allow('*', '*', 'loggedIn');
});
afterEach(async () => {
await app.destroy();
});
it('should init with camelCase scope key', async () => {
const Test = db.collection({
name: 'tests',
fields: [
{
type: 'string',
name: 'name',
},
{
type: 'string',
name: 'someField',
},
],
});
describe('main data source', () => {
it('should init with camelCase scope key', async () => {
const Test = db.collection({
name: 'tests',
fields: [
{
type: 'string',
name: 'name',
},
{
type: 'string',
name: 'someField',
},
],
});
await db.sync();
await Test.repository.create({
values: [
{
name: 't1',
someField: 'a',
},
{
name: 't2',
someField: 'b',
},
],
});
Test.setField('scopeKeySort', { type: 'sort', scopeKey: 'someField' });
await db.sync();
});
it('should init sorted value with thousand records', async () => {
const Test = db.collection({
name: 'tests',
fields: [
{
type: 'string',
name: 'name',
},
{
type: 'string',
name: 'group',
},
],
});
await db.sync();
await Test.model.bulkCreate(
(() => {
const values = [];
for (let i = 0; i < 100000; i++) {
values.push({
group: 'a',
name: `r${i}`,
});
values.push({
group: 'b',
name: `r${i}`,
});
}
return values;
})(),
);
Test.setField('sort', { type: 'sort', scopeKey: 'group' });
const begin = Date.now();
await db.sync();
const end = Date.now();
// log time cost as milliseconds
console.log(end - begin);
});
it('should init sorted value with null scopeValue', async () => {
const Test = db.collection({
name: 'tests',
fields: [
{
type: 'string',
name: 'name',
},
{
type: 'string',
name: 'group',
},
],
});
await db.sync();
await Test.repository.create({
values: [
{
group: null,
name: 'r5',
},
{
group: null,
name: 'r6',
},
],
});
Test.setField('sort', { type: 'sort', scopeKey: 'group' });
await db.sync();
});
it('should init sorted value with scopeKey', async () => {
const Test = db.collection({
name: 'tests',
fields: [
{
type: 'string',
name: 'name',
},
{
type: 'string',
name: 'group',
},
],
});
await db.sync();
await Test.repository.create({
values: [
{
group: 'a',
name: 'r1',
},
{
group: 'b',
name: 'r2',
},
{
group: 'a',
name: 'r3',
},
{
group: 'b',
name: 'r4',
},
{
group: null,
name: 'r5',
},
{
group: null,
name: 'r6',
},
],
});
Test.setField('sort', { type: 'sort', scopeKey: 'group' });
await db.sync();
const records = await Test.repository.find({});
const r3 = records.find((r) => r.get('name') === 'r3');
expect(r3.get('sort')).toBe(2);
const r5 = records.find((r) => r.get('name') === 'r5');
expect(r5.get('sort')).toBe(1);
});
it('should init sorted value by createdAt when primaryKey not exists', async () => {
const Test = db.collection({
autoGenId: false,
name: 'tests',
});
await db.sync();
await Test.repository.create({
values: [{}, {}, {}],
});
// add sort field
Test.setField('sort', { type: 'sort' });
let err;
try {
await db.sync();
} catch (e) {
err = e;
}
expect(err).toBeFalsy();
});
it('sort', async () => {
const Test = db.collection({
name: 'tests',
fields: [{ type: 'sort', name: 'sort' }],
await Test.repository.create({
values: [
{
name: 't1',
someField: 'a',
},
{
name: 't2',
someField: 'b',
},
],
});
Test.setField('scopeKeySort', { type: 'sort', scopeKey: 'someField' });
await db.sync();
});
await db.sync();
const test1 = await Test.model.create<any>();
expect(test1.sort).toBe(1);
const test2 = await Test.model.create<any>();
expect(test2.sort).toBe(2);
const test3 = await Test.model.create<any>();
expect(test3.sort).toBe(3);
});
it('should init sorted value with thousand records', async () => {
const Test = db.collection({
name: 'tests',
fields: [
{
type: 'string',
name: 'name',
},
{
type: 'string',
name: 'group',
},
],
});
it('should init sort value on data already exits', async () => {
const Test = db.collection({
name: 'tests',
fields: [
{
type: 'string',
name: 'name',
await db.sync();
await Test.model.bulkCreate(
(() => {
const values = [];
for (let i = 0; i < 100000; i++) {
values.push({
group: 'a',
name: `r${i}`,
});
values.push({
group: 'b',
name: `r${i}`,
});
}
return values;
})(),
);
Test.setField('sort', { type: 'sort', scopeKey: 'group' });
const begin = Date.now();
await db.sync();
const end = Date.now();
// log time cost as milliseconds
console.log(end - begin);
});
it('should init sorted value with null scopeValue', async () => {
const Test = db.collection({
name: 'tests',
fields: [
{
type: 'string',
name: 'name',
},
{
type: 'string',
name: 'group',
},
],
});
await db.sync();
await Test.repository.create({
values: [
{
group: null,
name: 'r5',
},
{
group: null,
name: 'r6',
},
],
});
Test.setField('sort', { type: 'sort', scopeKey: 'group' });
await db.sync();
});
it('should init sorted value with scopeKey', async () => {
const Test = db.collection({
name: 'tests',
fields: [
{
type: 'string',
name: 'name',
},
{
type: 'string',
name: 'group',
},
],
});
await db.sync();
await Test.repository.create({
values: [
{
group: 'a',
name: 'r1',
},
{
group: 'b',
name: 'r2',
},
{
group: 'a',
name: 'r3',
},
{
group: 'b',
name: 'r4',
},
{
group: null,
name: 'r5',
},
{
group: null,
name: 'r6',
},
],
});
Test.setField('sort', { type: 'sort', scopeKey: 'group' });
await db.sync();
const records = await Test.repository.find({});
const r3 = records.find((r) => r.get('name') === 'r3');
expect(r3.get('sort')).toBe(2);
const r5 = records.find((r) => r.get('name') === 'r5');
expect(r5.get('sort')).toBe(1);
});
it('should init sorted value by createdAt when primaryKey not exists', async () => {
const Test = db.collection({
autoGenId: false,
name: 'tests',
});
await db.sync();
await Test.repository.create({
values: [{}, {}, {}],
});
// add sort field
Test.setField('sort', { type: 'sort' });
let err;
try {
await db.sync();
} catch (e) {
err = e;
}
expect(err).toBeFalsy();
});
it('sort', async () => {
const Test = db.collection({
name: 'tests',
fields: [{ type: 'sort', name: 'sort' }],
});
await db.sync();
const test1 = await Test.model.create<any>();
expect(test1.sort).toBe(1);
const test2 = await Test.model.create<any>();
expect(test2.sort).toBe(2);
const test3 = await Test.model.create<any>();
expect(test3.sort).toBe(3);
});
it('should init sort value on data already exits', async () => {
const Test = db.collection({
name: 'tests',
fields: [
{
type: 'string',
name: 'name',
},
],
});
await db.sync();
await db.getRepository('tests').create({
values: {
name: 't1',
},
],
});
await db.getRepository('tests').create({
values: {
name: 't2',
},
});
await db.getRepository('tests').create({
values: {
name: 't3',
},
});
const field = Test.addField('sort', { type: 'sort' });
await field.sync({});
const items = await db.getRepository('tests').find({
order: ['id'],
});
expect(items.map((item) => item.get('sort'))).toEqual([1, 2, 3]);
});
await db.sync();
test.skip('simultaneously create ', async () => {
const Test = db.collection({
name: 'tests',
fields: [{ type: 'sort', name: 'sort' }],
});
await db.getRepository('tests').create({
values: {
name: 't1',
},
});
await db.getRepository('tests').create({
values: {
name: 't2',
},
});
await db.getRepository('tests').create({
values: {
name: 't3',
},
await db.sync();
const promise = [];
for (let i = 0; i < 3; i++) {
promise.push(Test.model.create());
}
await Promise.all(promise);
const tests = await Test.model.findAll();
const sortValues = tests.map((t) => t.get('sort')).sort();
expect(sortValues).toEqual([1, 2, 3]);
});
const field = Test.addField('sort', { type: 'sort' });
await field.sync({});
const items = await db.getRepository('tests').find({
order: ['id'],
it('skip if sort value not empty', async () => {
const Test = db.collection({
name: 'tests',
fields: [{ type: 'sort', name: 'sort' }],
});
await db.sync();
const test1 = await Test.model.create<any>({ sort: 3 });
expect(test1.sort).toBe(3);
const test2 = await Test.model.create<any>();
expect(test2.sort).toBe(4);
const test3 = await Test.model.create<any>();
expect(test3.sort).toBe(5);
});
it('scopeKey', async () => {
const Test = db.collection({
name: 'tests',
fields: [
{ type: 'sort', name: 'sort', scopeKey: 'status' },
{ type: 'string', name: 'status' },
],
});
await db.sync();
const t1 = await Test.model.create({ status: 'publish' });
const t2 = await Test.model.create({ status: 'publish' });
const t3 = await Test.model.create({ status: 'draft' });
const t4 = await Test.model.create({ status: 'draft' });
expect(t1.get('sort')).toBe(1);
expect(t2.get('sort')).toBe(2);
expect(t3.get('sort')).toBe(1);
expect(t4.get('sort')).toBe(2);
t1.set('status', 'draft');
await t1.save();
await t1.reload();
expect(t1.get('sort')).toBe(3);
});
expect(items.map((item) => item.get('sort'))).toEqual([1, 2, 3]);
});
test.skip('simultaneously create ', async () => {
const Test = db.collection({
name: 'tests',
fields: [{ type: 'sort', name: 'sort' }],
describe('external data source', () => {
it('create record', async () => {
anotherDB.collection({
name: 'posts',
fields: [
{ type: 'string', name: 'title' },
{ type: 'sort', name: 'sort' },
],
});
await anotherDB.sync();
const p1 = await anotherDB.getRepository('posts').create({
values: { title: 'p1' },
});
const p2 = await anotherDB.getRepository('posts').create({
values: { title: 'p2' },
});
expect(p1.sort).toBe(1);
expect(p2.sort).toBe(2);
});
await db.sync();
const promise = [];
for (let i = 0; i < 3; i++) {
promise.push(Test.model.create());
}
await Promise.all(promise);
const tests = await Test.model.findAll();
const sortValues = tests.map((t) => t.get('sort')).sort();
expect(sortValues).toEqual([1, 2, 3]);
});
it('skip if sort value not empty', async () => {
const Test = db.collection({
name: 'tests',
fields: [{ type: 'sort', name: 'sort' }],
});
await db.sync();
const test1 = await Test.model.create<any>({ sort: 3 });
expect(test1.sort).toBe(3);
const test2 = await Test.model.create<any>();
expect(test2.sort).toBe(4);
const test3 = await Test.model.create<any>();
expect(test3.sort).toBe(5);
});
it('scopeKey', async () => {
const Test = db.collection({
name: 'tests',
fields: [
{ type: 'sort', name: 'sort', scopeKey: 'status' },
{ type: 'string', name: 'status' },
],
});
await db.sync();
const t1 = await Test.model.create({ status: 'publish' });
const t2 = await Test.model.create({ status: 'publish' });
const t3 = await Test.model.create({ status: 'draft' });
const t4 = await Test.model.create({ status: 'draft' });
expect(t1.get('sort')).toBe(1);
expect(t2.get('sort')).toBe(2);
expect(t3.get('sort')).toBe(1);
expect(t4.get('sort')).toBe(2);
t1.set('status', 'draft');
await t1.save();
await t1.reload();
expect(t1.get('sort')).toBe(3);
});
});

View File

@ -20,12 +20,12 @@ export class PluginFieldSortServer extends Plugin {
const { lockManager } = this.app;
class SortFieldClass extends SortField {}
SortFieldClass.lockManager = lockManager;
this.app.db.registerFieldTypes({
sort: SortFieldClass,
});
this.app.dataSourceManager.beforeAddDataSource((dataSource: DataSource) => {
if (dataSource.collectionManager instanceof SequelizeCollectionManager) {
dataSource.collectionManager.db.registerFieldTypes({
sort: SortFieldClass,
});
dataSource.resourceManager.registerActionHandlers({ move });
}
});