fix: collection schema not exists (#2669)

* fix: collection schema not exists

* chore: test
This commit is contained in:
ChengLei Shao 2023-09-21 17:19:13 +08:00 committed by GitHub
parent 5f34970cfb
commit c88c91e908
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 168 additions and 2 deletions

View File

@ -285,7 +285,6 @@ export class Database extends EventEmitter implements AsyncEmitter {
migrations: this.migrations.callback(), migrations: this.migrations.callback(),
context, context,
storage: new SequelizeStorage({ storage: new SequelizeStorage({
modelName: `${this.options.tablePrefix || ''}migrations`,
...migratorOptions.storage, ...migratorOptions.storage,
sequelize: this.sequelize, sequelize: this.sequelize,
}), }),

View File

@ -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',
);
});
});

View File

@ -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');
}
}

View File

@ -222,6 +222,7 @@ export class CollectionManagerPlugin extends Plugin {
}); });
}; };
this.app.on('loadCollections', loadCollections);
this.app.on('beforeStart', loadCollections); this.app.on('beforeStart', loadCollections);
this.app.on('beforeUpgrade', async () => { this.app.on('beforeUpgrade', async () => {
const syncOptions = { const syncOptions = {

View File

@ -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', () => { pgOnly()('collection sync', () => {
let mainDb: Database; let mainDb: Database;
let mainApp: MockServer; let mainApp: MockServer;
@ -301,7 +369,7 @@ pgOnly()('collection sync', () => {
expect(user).toBeTruthy(); 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({ const subApp1Record = await mainDb.getRepository('applications').create({
values: { values: {
name: 'sub1', 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 () => { it('should support syncToApps with wildcard value', async () => {
const subApp1Record = await mainDb.getRepository('applications').create({ const subApp1Record = await mainDb.getRepository('applications').create({
values: { values: {