mirror of
https://github.com/nocobase/nocobase
synced 2024-11-15 09:29:16 +00:00
feat: register collection sync logic (#3055)
This commit is contained in:
parent
cd76ae7e63
commit
76a225e354
@ -20,8 +20,8 @@ describe('collection factory', function () {
|
||||
static type = 'child';
|
||||
}
|
||||
|
||||
db.collectionFactory.registerCollectionType(ChildCollection, (options) => {
|
||||
return options.child == true;
|
||||
db.collectionFactory.registerCollectionType(ChildCollection, {
|
||||
condition: (options) => options.child,
|
||||
});
|
||||
|
||||
const collection = db.collectionFactory.createCollection({
|
||||
@ -37,4 +37,27 @@ describe('collection factory', function () {
|
||||
|
||||
expect(collection2).toBeInstanceOf(Collection);
|
||||
});
|
||||
|
||||
it('should register collection type with sync logic', async () => {
|
||||
class ChildCollection extends Collection {
|
||||
static type = 'child';
|
||||
}
|
||||
|
||||
const fn = jest.fn();
|
||||
|
||||
db.collectionFactory.registerCollectionType(ChildCollection, {
|
||||
condition: (options) => options.child,
|
||||
onSync(model, options) {
|
||||
fn();
|
||||
},
|
||||
});
|
||||
|
||||
const collection = db.collectionFactory.createCollection({
|
||||
name: 'child',
|
||||
child: true,
|
||||
});
|
||||
|
||||
await collection.sync();
|
||||
expect(fn).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
@ -1,28 +1,38 @@
|
||||
import { Collection, CollectionOptions } from './collection';
|
||||
import Database from './database';
|
||||
import { Model } from './model';
|
||||
|
||||
type CollectionTypeOptions = {
|
||||
condition: (options: CollectionOptions) => boolean;
|
||||
onSync?: (model: typeof Model, options: any) => Promise<void>;
|
||||
};
|
||||
|
||||
export class CollectionFactory {
|
||||
private collectionTypes: Array<{
|
||||
ctor: typeof Collection;
|
||||
condition: (options: CollectionOptions) => boolean;
|
||||
}> = [];
|
||||
// Using a Map with the collection subclass as the key and options as the value
|
||||
public collectionTypes: Map<typeof Collection, CollectionTypeOptions> = new Map();
|
||||
|
||||
constructor(private database: Database) {}
|
||||
|
||||
registerCollectionType(collectionClass: typeof Collection, condition: (options: CollectionOptions) => boolean) {
|
||||
this.collectionTypes.push({ ctor: collectionClass, condition });
|
||||
registerCollectionType(
|
||||
collectionClass: typeof Collection, // Using the collection class as the key
|
||||
options: CollectionTypeOptions,
|
||||
) {
|
||||
// Storing the options associated with the collection class
|
||||
this.collectionTypes.set(collectionClass, options);
|
||||
}
|
||||
|
||||
createCollection<T extends Collection>(options: CollectionOptions): T {
|
||||
let klass = Collection;
|
||||
for (const { ctor, condition } of this.collectionTypes) {
|
||||
if (condition(options)) {
|
||||
createCollection<T extends Collection>(collectionOptions: CollectionOptions): T {
|
||||
let klass: typeof Collection = Collection;
|
||||
|
||||
// Iterating over the map to find the right class based on the condition
|
||||
for (const [ctor, options] of this.collectionTypes) {
|
||||
if (options.condition(collectionOptions)) {
|
||||
klass = ctor;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return new klass(options, {
|
||||
return new klass(collectionOptions, {
|
||||
database: this.database,
|
||||
}) as T;
|
||||
}
|
||||
|
@ -313,16 +313,22 @@ export class Database extends EventEmitter implements AsyncEmitter {
|
||||
}
|
||||
|
||||
registerCollectionType() {
|
||||
this.collectionFactory.registerCollectionType(InheritedCollection, (options) => {
|
||||
return options.inherits && lodash.castArray(options.inherits).length > 0;
|
||||
this.collectionFactory.registerCollectionType(InheritedCollection, {
|
||||
condition: (options) => {
|
||||
return options.inherits && lodash.castArray(options.inherits).length > 0;
|
||||
},
|
||||
});
|
||||
|
||||
this.collectionFactory.registerCollectionType(ViewCollection, (options) => {
|
||||
return options.viewName || options.view;
|
||||
this.collectionFactory.registerCollectionType(ViewCollection, {
|
||||
condition: (options) => {
|
||||
return options.viewName || options.view;
|
||||
},
|
||||
});
|
||||
|
||||
this.collectionFactory.registerCollectionType(SqlCollection, (options) => {
|
||||
return options.sql;
|
||||
this.collectionFactory.registerCollectionType(SqlCollection, {
|
||||
condition: (options) => {
|
||||
return options.sql;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -31,6 +31,69 @@ export class Model<TModelAttributes extends {} = any, TCreationAttributes extend
|
||||
protected _changedWithAssociations = new Set();
|
||||
protected _previousDataValuesWithAssociations = {};
|
||||
|
||||
static async sync(options) {
|
||||
if (this.collection.isView()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.collection.options.sync === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
const collectionSyncOptions = this.database.collectionFactory.collectionTypes.get(this.collection.constructor)
|
||||
?.onSync;
|
||||
|
||||
if (collectionSyncOptions) {
|
||||
await collectionSyncOptions(this, options);
|
||||
return;
|
||||
}
|
||||
|
||||
const model = this as any;
|
||||
|
||||
const _schema = model._schema;
|
||||
|
||||
if (_schema && _schema != 'public') {
|
||||
await this.sequelize.query(`CREATE SCHEMA IF NOT EXISTS "${_schema}";`, {
|
||||
raw: true,
|
||||
transaction: options?.transaction,
|
||||
});
|
||||
}
|
||||
|
||||
// fix sequelize sync with model that not have any column
|
||||
if (Object.keys(model.tableAttributes).length === 0) {
|
||||
if (this.database.inDialect('sqlite', 'mysql')) {
|
||||
console.error(`Zero-column tables aren't supported in ${this.database.sequelize.getDialect()}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
const queryInterface = this.sequelize.queryInterface;
|
||||
|
||||
if (!queryInterface.patched) {
|
||||
const oldDescribeTable = queryInterface.describeTable;
|
||||
queryInterface.describeTable = async function (...args) {
|
||||
try {
|
||||
return await oldDescribeTable.call(this, ...args);
|
||||
} catch (err) {
|
||||
if (err.message.includes('No description found for')) {
|
||||
return [];
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
};
|
||||
queryInterface.patched = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.collection.isInherited()) {
|
||||
return SyncRunner.syncInheritModel(model, options);
|
||||
}
|
||||
|
||||
return SequelizeModel.sync.call(this, options);
|
||||
}
|
||||
|
||||
// TODO
|
||||
public toChangedWithAssociations() {
|
||||
// @ts-ignore
|
||||
@ -149,58 +212,4 @@ export class Model<TModelAttributes extends {} = any, TCreationAttributes extend
|
||||
|
||||
return lodash.orderBy(data, orderItems, orderDirections);
|
||||
}
|
||||
|
||||
static async sync(options) {
|
||||
if (this.collection.isView()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.collection.options.sync === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
const model = this as any;
|
||||
|
||||
const _schema = model._schema;
|
||||
|
||||
if (_schema && _schema != 'public') {
|
||||
await this.sequelize.query(`CREATE SCHEMA IF NOT EXISTS "${_schema}";`, {
|
||||
raw: true,
|
||||
transaction: options?.transaction,
|
||||
});
|
||||
}
|
||||
|
||||
// fix sequelize sync with model that not have any column
|
||||
if (Object.keys(model.tableAttributes).length === 0) {
|
||||
if (this.database.inDialect('sqlite', 'mysql')) {
|
||||
console.error(`Zero-column tables aren't supported in ${this.database.sequelize.getDialect()}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
const queryInterface = this.sequelize.queryInterface;
|
||||
|
||||
if (!queryInterface.patched) {
|
||||
const oldDescribeTable = queryInterface.describeTable;
|
||||
queryInterface.describeTable = async function (...args) {
|
||||
try {
|
||||
return await oldDescribeTable.call(this, ...args);
|
||||
} catch (err) {
|
||||
if (err.message.includes('No description found for')) {
|
||||
return [];
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
};
|
||||
queryInterface.patched = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.collection.isInherited()) {
|
||||
return SyncRunner.syncInheritModel(model, options);
|
||||
}
|
||||
|
||||
return SequelizeModel.sync.call(this, options);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user