feat: add namespace and duplicator parameters for collection options (#1449)

* feat: add namespace and duplicator parameters for collection options

* fix: duplicator:getDict
This commit is contained in:
chenos 2023-02-13 09:57:03 +08:00 committed by GitHub
parent 2cfdfd2084
commit e5e503fe87
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
45 changed files with 186 additions and 42 deletions

View File

@ -7,7 +7,7 @@ import {
QueryInterfaceDropTableOptions, QueryInterfaceDropTableOptions,
SyncOptions, SyncOptions,
Transactionable, Transactionable,
Utils, Utils
} from 'sequelize'; } from 'sequelize';
import { Database } from './database'; import { Database } from './database';
import { Field, FieldOptions } from './fields'; import { Field, FieldOptions } from './fields';
@ -307,6 +307,7 @@ export class Collection<
updateOptions(options: CollectionOptions, mergeOptions?: any) { updateOptions(options: CollectionOptions, mergeOptions?: any) {
let newOptions = lodash.cloneDeep(options); let newOptions = lodash.cloneDeep(options);
newOptions = merge(this.options, newOptions, mergeOptions); newOptions = merge(this.options, newOptions, mergeOptions);
this.options = newOptions;
this.context.database.emit('beforeUpdateCollection', this, newOptions); this.context.database.emit('beforeUpdateCollection', this, newOptions);

View File

@ -15,7 +15,7 @@ import {
Sequelize, Sequelize,
SyncOptions, SyncOptions,
Transactionable, Transactionable,
Utils, Utils
} from 'sequelize'; } from 'sequelize';
import { SequelizeStorage, Umzug } from 'umzug'; import { SequelizeStorage, Umzug } from 'umzug';
import { Collection, CollectionOptions, RepositoryType } from './collection'; import { Collection, CollectionOptions, RepositoryType } from './collection';
@ -58,7 +58,7 @@ import {
SyncListener, SyncListener,
UpdateListener, UpdateListener,
UpdateWithAssociationsListener, UpdateWithAssociationsListener,
ValidateListener, ValidateListener
} from './types'; } from './types';
export interface MergeOptions extends merge.Options {} export interface MergeOptions extends merge.Options {}
@ -250,6 +250,8 @@ export class Database extends EventEmitter implements AsyncEmitter {
name: 'migrations', name: 'migrations',
autoGenId: false, autoGenId: false,
timestamps: false, timestamps: false,
namespace: 'core',
duplicator: 'required',
fields: [{ type: 'string', name: 'name' }], fields: [{ type: 'string', name: 'name' }],
}); });
@ -612,6 +614,7 @@ export class Database extends EventEmitter implements AsyncEmitter {
} }
extendCollection(collectionOptions: CollectionOptions, mergeOptions?: MergeOptions) { extendCollection(collectionOptions: CollectionOptions, mergeOptions?: MergeOptions) {
collectionOptions = lodash.cloneDeep(collectionOptions);
const collectionName = collectionOptions.name; const collectionName = collectionOptions.name;
const existCollection = this.getCollection(collectionName); const existCollection = this.getCollection(collectionName);
if (existCollection) { if (existCollection) {

View File

@ -101,6 +101,8 @@ export class ApplicationVersion {
if (!app.db.hasCollection('applicationVersion')) { if (!app.db.hasCollection('applicationVersion')) {
app.db.collection({ app.db.collection({
name: 'applicationVersion', name: 'applicationVersion',
namespace: 'core',
duplicator: 'required',
timestamps: false, timestamps: false,
fields: [{ name: 'value', type: 'string' }], fields: [{ name: 'value', type: 'string' }],
}); });

View File

@ -1,5 +1,7 @@
export default { export default {
name: 'applicationPlugins', name: 'applicationPlugins',
namespace: 'core',
duplicator: 'required',
repository: 'PluginManagerRepository', repository: 'PluginManagerRepository',
fields: [ fields: [
{ type: 'string', name: 'name', unique: true }, { type: 'string', name: 'name', unique: true },

View File

@ -2,5 +2,7 @@ import { CollectionOptions } from '@nocobase/database';
export default { export default {
name: 'rolesUsers', name: 'rolesUsers',
duplicator: 'optional',
namespace: 'acl',
fields: [{ type: 'boolean', name: 'default' }], fields: [{ type: 'boolean', name: 'default' }],
} as CollectionOptions; } as CollectionOptions;

View File

@ -1,6 +1,8 @@
import { CollectionOptions } from '@nocobase/database'; import { CollectionOptions } from '@nocobase/database';
export default { export default {
namespace: 'acl',
duplicator: 'required',
name: 'roles', name: 'roles',
title: '{{t("Roles")}}', title: '{{t("Roles")}}',
autoGenId: false, autoGenId: false,

View File

@ -1,6 +1,8 @@
import { CollectionOptions } from '@nocobase/database'; import { CollectionOptions } from '@nocobase/database';
export default { export default {
namespace: 'acl',
duplicator: 'required',
name: 'rolesResources', name: 'rolesResources',
model: 'RoleResourceModel', model: 'RoleResourceModel',
indexes: [ indexes: [

View File

@ -1,6 +1,8 @@
import { CollectionOptions } from '@nocobase/database'; import { CollectionOptions } from '@nocobase/database';
export default { export default {
namespace: 'acl',
duplicator: 'required',
name: 'rolesResourcesActions', name: 'rolesResourcesActions',
model: 'RoleResourceActionModel', model: 'RoleResourceActionModel',
fields: [ fields: [

View File

@ -1,6 +1,8 @@
import { CollectionOptions } from '@nocobase/database'; import { CollectionOptions } from '@nocobase/database';
export default { export default {
namespace: 'acl',
duplicator: 'required',
name: 'rolesResourcesScopes', name: 'rolesResourcesScopes',
fields: [ fields: [
{ {

View File

@ -772,6 +772,11 @@ export class PluginACL extends Plugin {
async load() { async load() {
await this.importCollections(resolve(__dirname, 'collections')); await this.importCollections(resolve(__dirname, 'collections'));
this.db.extendCollection({
name: 'rolesUischemas',
namespace: 'acl',
duplicator: 'required',
});
} }
} }

View File

@ -1,6 +1,8 @@
import { defineCollection } from '@nocobase/database'; import { defineCollection } from '@nocobase/database';
export default defineCollection({ export default defineCollection({
namespace: 'audit-logs',
duplicator: 'optional',
name: 'auditChanges', name: 'auditChanges',
title: '变动值', title: '变动值',
createdBy: false, createdBy: false,

View File

@ -1,6 +1,8 @@
import { defineCollection } from '@nocobase/database'; import { defineCollection } from '@nocobase/database';
export default defineCollection({ export default defineCollection({
namespace: 'audit-logs',
duplicator: 'optional',
name: 'auditLogs', name: 'auditLogs',
createdBy: false, createdBy: false,
updatedBy: false, updatedBy: false,

View File

@ -1,6 +1,8 @@
import { defineCollection } from '@nocobase/database'; import { defineCollection } from '@nocobase/database';
export default defineCollection({ export default defineCollection({
namespace: 'china-region',
duplicator: 'skip',
name: 'chinaRegions', name: 'chinaRegions',
title: '中国行政区划', title: '中国行政区划',
autoGenId: false, autoGenId: false,

View File

@ -1,6 +1,8 @@
import { CollectionOptions } from '@nocobase/database'; import { CollectionOptions } from '@nocobase/database';
export default { export default {
namespace: 'collection-manager',
duplicator: 'required',
name: 'collectionCategories', name: 'collectionCategories',
autoGenId: true, autoGenId: true,
sortable: true, sortable: true,

View File

@ -1,6 +1,8 @@
import { CollectionOptions } from '@nocobase/database'; import { CollectionOptions } from '@nocobase/database';
export default { export default {
namespace: 'collection-manager',
duplicator: 'required',
name: 'collections', name: 'collections',
title: '数据表配置', title: '数据表配置',
sortable: 'sort', sortable: 'sort',

View File

@ -1,6 +1,8 @@
import { CollectionOptions } from '@nocobase/database'; import { CollectionOptions } from '@nocobase/database';
export default { export default {
namespace: 'collection-manager',
duplicator: 'required',
name: 'fields', name: 'fields',
autoGenId: false, autoGenId: false,
model: 'FieldModel', model: 'FieldModel',

View File

@ -245,6 +245,12 @@ export class CollectionManagerPlugin extends Plugin {
} }
await next(); await next();
}); });
this.app.db.extendCollection({
name: 'collectionCategory',
namespace: 'collection-manager',
duplicator: 'required',
});
} }
} }

View File

@ -1,4 +1,3 @@
import { Application } from '@nocobase/server';
import lodash from 'lodash'; import lodash from 'lodash';
import { Restorer } from './restorer'; import { Restorer } from './restorer';
@ -70,7 +69,7 @@ CollectionGroupManager.registerCollectionGroup({
CollectionGroupManager.registerCollectionGroup({ CollectionGroupManager.registerCollectionGroup({
pluginName: 'collection-manager', pluginName: 'collection-manager',
function: 'collections', function: 'collections',
collections: ['collections', 'fields'], collections: ['collections', 'fields', 'collectionCategories', 'collectionCategory'],
dumpable: 'required', dumpable: 'required',
}); });
@ -252,5 +251,5 @@ CollectionGroupManager.registerCollectionGroup({
pluginName: 'iframe-block', pluginName: 'iframe-block',
function: 'iframe html storage', function: 'iframe html storage',
collections: ['iframeHtml'], collections: ['iframeHtml'],
dumpable: 'optional', dumpable: 'required',
}); });

View File

@ -11,4 +11,48 @@ export default class Duplicator extends Plugin {
addDumpCommand(this.app); addDumpCommand(this.app);
addRestoreCommand(this.app); addRestoreCommand(this.app);
} }
async load() {
this.app.resourcer.define({
name: 'duplicator',
actions: {
getDict: async (ctx, next) => {
ctx.withoutDataWrapping = true;
let collectionNames = await this.db.getRepository('collections').find();
collectionNames = collectionNames.map((item) => item.get('name'));
const collections: any[] = [];
for (const [name, collection] of this.db.collections) {
const columns: any[] = [];
for (const key in collection.model.rawAttributes) {
if (Object.prototype.hasOwnProperty.call(collection.model.rawAttributes, key)) {
const attribute = collection.model.rawAttributes[key];
columns.push({
realName: attribute.field,
name: key,
});
}
}
const item = {
name,
title: collection.options.title,
namespace: collection.options.namespace,
duplicator: collection.options.duplicator,
// columns,
};
if (!item.namespace && collectionNames.includes(name)) {
item.namespace = 'collection-manager';
if (!item.duplicator) {
item.duplicator = 'optional';
}
}
collections.push(item);
}
ctx.body = collections;
await next();
},
},
});
this.app.acl.allow('duplicator', 'getDict');
}
} }

View File

@ -1,6 +1,8 @@
import { CollectionOptions } from '@nocobase/database'; import { CollectionOptions } from '@nocobase/database';
export default { export default {
namespace: 'file-manager',
duplicator: 'optional',
name: 'attachments', name: 'attachments',
title: '文件管理器', title: '文件管理器',
createdBy: true, createdBy: true,

View File

@ -1,6 +1,8 @@
import { CollectionOptions } from '@nocobase/database'; import { CollectionOptions } from '@nocobase/database';
export default { export default {
namespace: 'file-manager',
duplicator: 'optional',
name: 'storages', name: 'storages',
title: '存储引擎', title: '存储引擎',
fields: [ fields: [

View File

@ -1,6 +1,8 @@
import { defineCollection } from '@nocobase/database'; import { defineCollection } from '@nocobase/database';
export default defineCollection({ export default defineCollection({
namespace: 'graph-collection-manager',
duplicator: 'required',
name: 'graphPositions', name: 'graphPositions',
fields: [ fields: [
{ {

View File

@ -1,6 +1,8 @@
import { CollectionOptions } from '@nocobase/database'; import { CollectionOptions } from '@nocobase/database';
export default { export default {
namespace: 'iframe-block',
duplicator: 'required',
name: 'iframeHtml', name: 'iframeHtml',
createdBy: true, createdBy: true,
updatedBy: true, updatedBy: true,

View File

@ -2,6 +2,8 @@ import { CollectionOptions } from "@nocobase/client";
import { MapConfigurationCollectionName } from "../constants"; import { MapConfigurationCollectionName } from "../constants";
export default { export default {
namespace: 'map',
duplicator: 'optional',
name: MapConfigurationCollectionName, name: MapConfigurationCollectionName,
title: '{{t("Map Manager")}}', title: '{{t("Map Manager")}}',
fields: [ fields: [

View File

@ -1,6 +1,8 @@
import { defineCollection } from '@nocobase/database'; import { defineCollection } from '@nocobase/database';
export default defineCollection({ export default defineCollection({
namespace: 'multi-app-manager',
duplicator: 'optional',
name: 'applications', name: 'applications',
model: 'ApplicationModel', model: 'ApplicationModel',
autoGenId: false, autoGenId: false,

View File

@ -1,6 +1,8 @@
import { CollectionOptions } from '@nocobase/database'; import { CollectionOptions } from '@nocobase/database';
export default { export default {
namespace: 'oidc',
duplicator: 'optional',
name: 'oidcProviders', name: 'oidcProviders',
title: '{{t("OIDC Providers")}}', title: '{{t("OIDC Providers")}}',
fields: [ fields: [

View File

@ -1,6 +1,8 @@
import { CollectionOptions } from '@nocobase/database'; import { CollectionOptions } from '@nocobase/database';
export default { export default {
namespace: 'saml',
duplicator: 'optional',
name: 'samlProviders', name: 'samlProviders',
title: '{{t("SAML Providers")}}', title: '{{t("SAML Providers")}}',
fields: [ fields: [

View File

@ -1,4 +1,6 @@
export default { export default {
namespace: 'sequence-field',
duplicator: 'required',
name: 'sequences', name: 'sequences',
fields: [ fields: [
{ {

View File

@ -1,6 +1,8 @@
import { CollectionOptions } from '@nocobase/database'; import { CollectionOptions } from '@nocobase/database';
export default { export default {
namespace: 'snapshot-field',
duplicator: 'required',
name: 'collectionsHistory', name: 'collectionsHistory',
title: '数据表历史', title: '数据表历史',
sortable: 'sort', sortable: 'sort',

View File

@ -1,6 +1,8 @@
import { CollectionOptions } from '@nocobase/database'; import { CollectionOptions } from '@nocobase/database';
export default { export default {
namespace: 'snapshot-field',
duplicator: 'required',
name: 'fieldsHistory', name: 'fieldsHistory',
title: '{{t("Fields history")}}', title: '{{t("Fields history")}}',
autoGenId: false, autoGenId: false,

View File

@ -1,6 +1,8 @@
import { defineCollection } from '@nocobase/database'; import { defineCollection } from '@nocobase/database';
export default defineCollection({ export default defineCollection({
namespace: 'system-settings',
duplicator: 'optional',
name: 'systemSettings', name: 'systemSettings',
fields: [ fields: [
{ {

View File

@ -1,6 +1,8 @@
import { defineCollection } from '@nocobase/database'; import { defineCollection } from '@nocobase/database';
export default defineCollection({ export default defineCollection({
namespace: 'ui-routes-storage',
duplicator: 'required',
name: 'uiRoutes', name: 'uiRoutes',
title: '前端路由表', title: '前端路由表',
model: 'MagicAttributeModel', model: 'MagicAttributeModel',

View File

@ -1,6 +1,8 @@
import { CollectionOptions } from '@nocobase/database'; import { CollectionOptions } from '@nocobase/database';
export default { export default {
namespace: 'ui-schema-storage',
duplicator: 'required',
name: 'uiSchemaServerHooks', name: 'uiSchemaServerHooks',
model: 'ServerHookModel', model: 'ServerHookModel',
// autoGenId: false, // autoGenId: false,

View File

@ -1,6 +1,8 @@
import { defineCollection } from '@nocobase/database'; import { defineCollection } from '@nocobase/database';
export default defineCollection({ export default defineCollection({
namespace: 'ui-schema-storage',
duplicator: 'required',
name: 'uiSchemaTemplates', name: 'uiSchemaTemplates',
autoGenId: false, autoGenId: false,
fields: [ fields: [

View File

@ -1,6 +1,8 @@
import { CollectionOptions } from '@nocobase/database'; import { CollectionOptions } from '@nocobase/database';
export default { export default {
namespace: 'ui-schema-storage',
duplicator: 'required',
name: 'uiSchemaTreePath', name: 'uiSchemaTreePath',
autoGenId: false, autoGenId: false,
timestamps: false, timestamps: false,

View File

@ -1,6 +1,8 @@
import { CollectionOptions } from '@nocobase/database'; import { CollectionOptions } from '@nocobase/database';
export default { export default {
namespace: 'ui-schema-storage',
duplicator: 'required',
name: 'uiSchemas', name: 'uiSchemas',
title: '字段配置', title: '字段配置',
autoGenId: false, autoGenId: false,

View File

@ -1,6 +1,8 @@
import { CollectionOptions } from '@nocobase/database'; import { CollectionOptions } from '@nocobase/database';
export default { export default {
namespace: 'users',
duplicator: 'optional',
name: 'users', name: 'users',
title: '{{t("Users")}}', title: '{{t("Users")}}',
sortable: 'sort', sortable: 'sort',

View File

@ -1,4 +1,6 @@
export default { export default {
namespace: 'verification',
duplicator: 'optional',
name: 'verifications', name: 'verifications',
fields: [ fields: [
{ {

View File

@ -1,4 +1,6 @@
export default { export default {
namespace: 'verification',
duplicator: 'optional',
name: 'verifications_providers', name: 'verifications_providers',
fields: [ fields: [
{ {

View File

@ -15,7 +15,6 @@ import WorkflowModel from './models/Workflow';
import Processor from './Processor'; import Processor from './Processor';
import initTriggers, { Trigger } from './triggers'; import initTriggers, { Trigger } from './triggers';
type Pending = [ExecutionModel, JobModel?]; type Pending = [ExecutionModel, JobModel?];
export default class WorkflowPlugin extends Plugin { export default class WorkflowPlugin extends Plugin {
instructions: Registry<Instruction> = new Registry(); instructions: Registry<Instruction> = new Registry();
@ -34,9 +33,9 @@ export default class WorkflowPlugin extends Plugin {
} else if (!instance.current) { } else if (!instance.current) {
const count = await Model.count({ const count = await Model.count({
where: { where: {
key: instance.key key: instance.key,
}, },
transaction: options.transaction transaction: options.transaction,
}); });
if (!count) { if (!count) {
instance.set('current', true); instance.set('current', true);
@ -52,18 +51,21 @@ export default class WorkflowPlugin extends Plugin {
key: instance.key, key: instance.key,
current: true, current: true,
id: { id: {
[Op.ne]: instance.id [Op.ne]: instance.id,
} },
}, },
transaction: options.transaction transaction: options.transaction,
}); });
if (previous) { if (previous) {
// NOTE: set to `null` but not `false` will not violate the unique index // NOTE: set to `null` but not `false` will not violate the unique index
await previous.update({ enabled: false, current: null }, { await previous.update(
transaction: options.transaction, { enabled: false, current: null },
hooks: false {
}); transaction: options.transaction,
hooks: false,
},
);
this.toggle(previous, false); this.toggle(previous, false);
} }
@ -185,24 +187,29 @@ export default class WorkflowPlugin extends Plugin {
// NOTE: no transaction here for read-uncommitted execution // NOTE: no transaction here for read-uncommitted execution
const existed = await workflow.countExecutions({ const existed = await workflow.countExecutions({
where: { where: {
id: options.context.executionId id: options.context.executionId,
} },
}); });
if (existed) { if (existed) {
this.app.logger.warn(`[Workflow] workflow ${workflow.id} has already been triggered in same execution (${options.context.executionId}), and newly triggering will be skipped.`); this.app.logger.warn(
`[Workflow] workflow ${workflow.id} has already been triggered in same execution (${options.context.executionId}), and newly triggering will be skipped.`,
);
valid = false; valid = false;
} }
} }
if (valid) { if (valid) {
const execution = await this.db.sequelize.transaction(async transaction => { const execution = await this.db.sequelize.transaction(async (transaction) => {
const execution = await workflow.createExecution({ const execution = await workflow.createExecution(
context, {
key: workflow.key, context,
status: EXECUTION_STATUS.CREATED, key: workflow.key,
useTransaction: workflow.useTransaction, status: EXECUTION_STATUS.CREATED,
}, { transaction }); useTransaction: workflow.useTransaction,
},
{ transaction },
);
const executed = await workflow.countExecutions({ transaction }); const executed = await workflow.countExecutions({ transaction });
@ -211,19 +218,22 @@ export default class WorkflowPlugin extends Plugin {
const allExecuted = await (<typeof ExecutionModel>execution.constructor).count({ const allExecuted = await (<typeof ExecutionModel>execution.constructor).count({
where: { where: {
key: workflow.key key: workflow.key,
}, },
transaction transaction,
}); });
await (<typeof WorkflowModel>workflow.constructor).update({ await (<typeof WorkflowModel>workflow.constructor).update(
allExecuted {
}, { allExecuted,
where: {
key: workflow.key
}, },
individualHooks: true, {
transaction where: {
}); key: workflow.key,
},
individualHooks: true,
transaction,
},
);
execution.workflow = workflow; execution.workflow = workflow;
@ -243,7 +253,7 @@ export default class WorkflowPlugin extends Plugin {
} else { } else {
this.dispatch(); this.dispatch();
} }
} };
public async resume(job) { public async resume(job) {
if (!job.execution) { if (!job.execution) {
@ -264,16 +274,16 @@ export default class WorkflowPlugin extends Plugin {
if (this.pending.length) { if (this.pending.length) {
next = this.pending.shift() as Pending; next = this.pending.shift() as Pending;
} else { } else {
const execution = await this.db.getRepository('executions').findOne({ const execution = (await this.db.getRepository('executions').findOne({
filter: { filter: {
status: EXECUTION_STATUS.CREATED status: EXECUTION_STATUS.CREATED,
}, },
sort: 'createdAt' sort: 'createdAt',
}) as ExecutionModel; })) as ExecutionModel;
if (execution) { if (execution) {
next = [execution]; next = [execution];
} }
}; }
if (next) { if (next) {
this.process(...next); this.process(...next);
} }

View File

@ -1,6 +1,8 @@
import { CollectionOptions } from '@nocobase/database'; import { CollectionOptions } from '@nocobase/database';
export default { export default {
namespace: 'workflow',
duplicator: 'optional',
name: 'executions', name: 'executions',
fields: [ fields: [
{ {

View File

@ -1,6 +1,8 @@
import { CollectionOptions } from '@nocobase/database'; import { CollectionOptions } from '@nocobase/database';
export default { export default {
namespace: 'workflow',
duplicator: 'required',
name: 'flow_nodes', name: 'flow_nodes',
// model: 'FlowNodeModel', // model: 'FlowNodeModel',
title: 'Workflow Nodes', title: 'Workflow Nodes',

View File

@ -1,6 +1,8 @@
import { CollectionOptions } from '@nocobase/database'; import { CollectionOptions } from '@nocobase/database';
export default { export default {
namespace: 'workflow',
duplicator: 'optional',
name: 'jobs', name: 'jobs',
fields: [ fields: [
{ {

View File

@ -2,6 +2,8 @@ import { CollectionOptions } from '@nocobase/database';
export default function () { export default function () {
return { return {
namespace: 'workflow',
duplicator: 'required',
name: 'workflows', name: 'workflows',
fields: [ fields: [
{ {

View File

@ -1,7 +1,9 @@
import { CollectionOptions } from '@nocobase/database'; import { CollectionOptions } from '@nocobase/database';
export default { export default {
namespace: 'workflow',
name: 'users_jobs', name: 'users_jobs',
duplicator: 'optional',
fields: [ fields: [
{ {
type: 'bigInt', type: 'bigInt',