From 7b715c847d6a6535e54838176b1d15d59caa38d4 Mon Sep 17 00:00:00 2001 From: Junyi Date: Thu, 13 Feb 2025 22:29:58 +0800 Subject: [PATCH] fix(plugin-field-sort): fix field registering for external data source (#6212) --- .../src/server/__tests__/sort.test.ts | 624 ++++++++++-------- .../plugin-field-sort/src/server/plugin.ts | 6 +- 2 files changed, 338 insertions(+), 292 deletions(-) diff --git a/packages/plugins/@nocobase/plugin-field-sort/src/server/__tests__/sort.test.ts b/packages/plugins/@nocobase/plugin-field-sort/src/server/__tests__/sort.test.ts index 536ab946dd..8185c072e3 100644 --- a/packages/plugins/@nocobase/plugin-field-sort/src/server/__tests__/sort.test.ts +++ b/packages/plugins/@nocobase/plugin-field-sort/src/server/__tests__/sort.test.ts @@ -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(); - expect(test1.sort).toBe(1); - const test2 = await Test.model.create(); - expect(test2.sort).toBe(2); - const test3 = await Test.model.create(); - 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(); + expect(test1.sort).toBe(1); + const test2 = await Test.model.create(); + expect(test2.sort).toBe(2); + const test3 = await Test.model.create(); + 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({ sort: 3 }); + expect(test1.sort).toBe(3); + const test2 = await Test.model.create(); + expect(test2.sort).toBe(4); + const test3 = await Test.model.create(); + 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({ sort: 3 }); - expect(test1.sort).toBe(3); - const test2 = await Test.model.create(); - expect(test2.sort).toBe(4); - const test3 = await Test.model.create(); - 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); }); }); diff --git a/packages/plugins/@nocobase/plugin-field-sort/src/server/plugin.ts b/packages/plugins/@nocobase/plugin-field-sort/src/server/plugin.ts index 16f705eb21..888834b9ed 100644 --- a/packages/plugins/@nocobase/plugin-field-sort/src/server/plugin.ts +++ b/packages/plugins/@nocobase/plugin-field-sort/src/server/plugin.ts @@ -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 }); } });