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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -772,6 +772,11 @@ export class PluginACL extends Plugin {
async load() {
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';
export default defineCollection({
namespace: 'audit-logs',
duplicator: 'optional',
name: 'auditChanges',
title: '变动值',
createdBy: false,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -245,6 +245,12 @@ export class CollectionManagerPlugin extends Plugin {
}
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 { Restorer } from './restorer';
@ -70,7 +69,7 @@ CollectionGroupManager.registerCollectionGroup({
CollectionGroupManager.registerCollectionGroup({
pluginName: 'collection-manager',
function: 'collections',
collections: ['collections', 'fields'],
collections: ['collections', 'fields', 'collectionCategories', 'collectionCategory'],
dumpable: 'required',
});
@ -252,5 +251,5 @@ CollectionGroupManager.registerCollectionGroup({
pluginName: 'iframe-block',
function: 'iframe html storage',
collections: ['iframeHtml'],
dumpable: 'optional',
dumpable: 'required',
});

View File

@ -11,4 +11,48 @@ export default class Duplicator extends Plugin {
addDumpCommand(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';
export default {
namespace: 'file-manager',
duplicator: 'optional',
name: 'attachments',
title: '文件管理器',
createdBy: true,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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