diff --git a/packages/core/database/src/database.ts b/packages/core/database/src/database.ts index 25a9f343f..1b65ea269 100644 --- a/packages/core/database/src/database.ts +++ b/packages/core/database/src/database.ts @@ -285,7 +285,6 @@ export class Database extends EventEmitter implements AsyncEmitter { migrations: this.migrations.callback(), context, storage: new SequelizeStorage({ - modelName: `${this.options.tablePrefix || ''}migrations`, ...migratorOptions.storage, sequelize: this.sequelize, }), diff --git a/packages/plugins/@nocobase/plugin-collection-manager/src/server/__tests__/migrations/set-collection-schema.test.ts b/packages/plugins/@nocobase/plugin-collection-manager/src/server/__tests__/migrations/set-collection-schema.test.ts new file mode 100644 index 000000000..6e7d361be --- /dev/null +++ b/packages/plugins/@nocobase/plugin-collection-manager/src/server/__tests__/migrations/set-collection-schema.test.ts @@ -0,0 +1,42 @@ +import { Database, MigrationContext } from '@nocobase/database'; +import { MockServer, pgOnly } from '@nocobase/test'; +import Migrator from '../../migrations/20230918024546-set-collection-schema'; +import { createApp } from '../index'; + +pgOnly()('set collection schema', () => { + let app: MockServer; + let db: Database; + + beforeEach(async () => { + app = await createApp({}); + + db = app.db; + }); + + afterEach(async () => { + await app.destroy(); + }); + + it('should set collection schema', async () => { + const collection = await db.getRepository('collections').create({ + values: { + name: 'testCollection', + }, + }); + + await collection.set('schema', undefined); + await collection.save(); + + expect(collection.options.schema).toBeUndefined(); + + const migration = new Migrator({ db } as MigrationContext); + migration.context.app = app; + await migration.up(); + + const collection2 = await db.getRepository('collections').findOne({}); + + expect(collection2.options.schema).toEqual( + process.env.COLLECTION_MANAGER_SCHEMA || app.db.options.schema || 'public', + ); + }); +}); diff --git a/packages/plugins/@nocobase/plugin-collection-manager/src/server/migrations/20230918024546-set-collection-schema.ts b/packages/plugins/@nocobase/plugin-collection-manager/src/server/migrations/20230918024546-set-collection-schema.ts new file mode 100644 index 000000000..2a4f13639 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-collection-manager/src/server/migrations/20230918024546-set-collection-schema.ts @@ -0,0 +1,28 @@ +import { Migration } from '@nocobase/server'; + +export default class extends Migration { + async up() { + if (!this.db.inDialect('postgres')) { + return; + } + + if (this.context.app.name !== 'main') { + return; + } + + // find collections that not set schema + const userCollections = await this.db.getRepository('collections').find({ + filter: { + 'options.schema': null, + 'options.from.$ne': 'db2cm', + }, + }); + + for (const collection of userCollections) { + await collection.set('schema', process.env.COLLECTION_MANAGER_SCHEMA || this.db.options.schema || 'public'); + await collection.save(); + } + + await this.context.app.emitAsync('loadCollections'); + } +} diff --git a/packages/plugins/@nocobase/plugin-collection-manager/src/server/server.ts b/packages/plugins/@nocobase/plugin-collection-manager/src/server/server.ts index 0fbfbc500..12d7392d4 100644 --- a/packages/plugins/@nocobase/plugin-collection-manager/src/server/server.ts +++ b/packages/plugins/@nocobase/plugin-collection-manager/src/server/server.ts @@ -222,6 +222,7 @@ export class CollectionManagerPlugin extends Plugin { }); }; + this.app.on('loadCollections', loadCollections); this.app.on('beforeStart', loadCollections); this.app.on('beforeUpgrade', async () => { const syncOptions = { diff --git a/packages/plugins/@nocobase/plugin-multi-app-share-collection/src/server/__tests__/collection-sync.test.ts b/packages/plugins/@nocobase/plugin-multi-app-share-collection/src/server/__tests__/collection-sync.test.ts index 3c70e3b2d..0e3067c84 100644 --- a/packages/plugins/@nocobase/plugin-multi-app-share-collection/src/server/__tests__/collection-sync.test.ts +++ b/packages/plugins/@nocobase/plugin-multi-app-share-collection/src/server/__tests__/collection-sync.test.ts @@ -41,6 +41,74 @@ pgOnly()('enable plugin', () => { }); }); +pgOnly()('collection sync after main', () => { + let mainApp: MockServer; + + beforeEach(async () => { + const app = mockServer({ + acl: false, + plugins: ['nocobase'], + }); + + await app.load(); + await app.db.sequelize.query(`DROP SCHEMA IF EXISTS sub1 CASCADE`); + await app.db.sequelize.query(`DROP SCHEMA IF EXISTS sub2 CASCADE`); + + await app.install({ + clean: true, + }); + + await app.start(); + + mainApp = app; + }); + + afterEach(async () => { + await mainApp.destroy(); + }); + + it('should sync collections in sub app', async () => { + await mainApp.db.getRepository('collections').create({ + values: { + name: 'posts', + fields: [{ type: 'string', title: 'title' }], + }, + context: {}, + }); + + await mainApp.db.getRepository('posts').create({ + values: { + title: 'test', + }, + }); + + await mainApp.runCommand('pm', 'enable', 'multi-app-manager'); + await mainApp.runCommand('pm', 'enable', 'multi-app-share-collection'); + + await mainApp.db.sync(); + + const subApp1Record = await mainApp.db.getRepository('applications').create({ + values: { + name: 'sub1', + }, + context: { + waitSubAppInstall: true, + }, + }); + + const subApp1 = await AppSupervisor.getInstance().getApp(subApp1Record.name); + + await subApp1.runCommand('restart'); + const postCollection = subApp1.db.getCollection('posts'); + + expect(postCollection.options.schema).toBe( + process.env.COLLECTION_MANAGER_SCHEMA || mainApp.db.options.schema || 'public', + ); + + expect(await subApp1.db.getRepository('posts').count()).toBe(1); + }); +}); + pgOnly()('collection sync', () => { let mainDb: Database; let mainApp: MockServer; @@ -301,7 +369,7 @@ pgOnly()('collection sync', () => { expect(user).toBeTruthy(); }); - it('should sync custom collections', async () => { + it('should sync custom collections after sub app created', async () => { const subApp1Record = await mainDb.getRepository('applications').create({ values: { name: 'sub1', @@ -330,6 +398,34 @@ pgOnly()('collection sync', () => { ); }); + it('should sync custom collections before sub app created', async () => { + await mainApp.db.getRepository('collections').create({ + values: { + name: 'posts', + fields: [{ type: 'string', title: 'title' }], + }, + context: {}, + }); + + const subApp1Record = await mainDb.getRepository('applications').create({ + values: { + name: 'sub1', + }, + context: { + waitSubAppInstall: true, + }, + }); + + const subApp1 = await AppSupervisor.getInstance().getApp(subApp1Record.name); + + await subApp1.runCommand('restart'); + const postCollection = subApp1.db.getCollection('posts'); + + expect(postCollection.options.schema).toBe( + process.env.COLLECTION_MANAGER_SCHEMA || mainDb.options.schema || 'public', + ); + }); + it('should support syncToApps with wildcard value', async () => { const subApp1Record = await mainDb.getRepository('applications').create({ values: {