diff --git a/packages/actions/src/actions/associate.ts b/packages/actions/src/actions/associate.ts index 23ee1f183..71629a293 100644 --- a/packages/actions/src/actions/associate.ts +++ b/packages/actions/src/actions/associate.ts @@ -1,8 +1,13 @@ import { Context, Next } from '.'; - import { list, get, create, update, destroy } from './common'; -import { HasOne, BelongsTo, BelongsToMany, HasMany, Model, Relation } from '@nocobase/database'; -import { Op } from 'sequelize'; +import { + Model, + Relation, + HASONE, + BELONGSTO, + BELONGSTOMANY, + HASMANY, +} from '@nocobase/database'; /** * 建立关联 @@ -116,9 +121,9 @@ export async function remove(ctx: Context, next: Next) { const options = TargetModel.parseApiJson({ fields, }); - if (resourceField instanceof HasOne || resourceField instanceof BelongsTo) { + if (resourceField instanceof HASONE || resourceField instanceof BELONGSTO) { ctx.body = await associated[setAccessor](null); - } else if (resourceField instanceof HasMany || resourceField instanceof BelongsToMany) { + } else if (resourceField instanceof HASMANY || resourceField instanceof BELONGSTOMANY) { const [model]: Model[] = await associated[getAccessor]({ ...options, where: { @@ -153,7 +158,7 @@ export async function toggle(ctx: Context, next: Next) { const options = TargetModel.parseApiJson({ fields, }); - if (resourceField instanceof HasOne || resourceField instanceof BelongsTo) { + if (resourceField instanceof HASONE || resourceField instanceof BELONGSTO) { const m1 = await associated[getAccessor](); if (m1 && m1[resourceKeyAttribute || resourceField.options.targetKey || TargetModel.primaryKeyAttribute] == resourceKey) { ctx.body = await associated[setAccessor](null); @@ -168,7 +173,7 @@ export async function toggle(ctx: Context, next: Next) { }); ctx.body = await associated[setAccessor](m2); } - } else if (resourceField instanceof HasMany || resourceField instanceof BelongsToMany) { + } else if (resourceField instanceof HASMANY || resourceField instanceof BELONGSTOMANY) { const [model]: Model[] = await associated[getAccessor]({ ...options, where: { diff --git a/packages/actions/src/actions/common.ts b/packages/actions/src/actions/common.ts index be19482f4..537793f2f 100644 --- a/packages/actions/src/actions/common.ts +++ b/packages/actions/src/actions/common.ts @@ -1,5 +1,11 @@ import { Context, Next } from '.'; -import { Relation, Model, HasOne, HasMany, BelongsTo, BelongsToMany } from '@nocobase/database'; +import { + Model, + HASONE, + HASMANY, + BELONGSTO, + BELONGSTOMANY, +} from '@nocobase/database'; import { DEFAULT_PAGE, DEFAULT_PER_PAGE } from '@nocobase/resourcer'; import { Utils, Op } from 'sequelize'; import _ from 'lodash'; @@ -159,10 +165,10 @@ export async function get(ctx: Context, next: Next) { const options = TargetModel.parseApiJson({ fields, }); - if (resourceField instanceof HasOne || resourceField instanceof BelongsTo) { + if (resourceField instanceof HASONE || resourceField instanceof BELONGSTO) { const model: Model = await associated[getAccessor]({ ...options, context: ctx }); ctx.body = model; - } else if (resourceField instanceof HasMany || resourceField instanceof BelongsToMany) { + } else if (resourceField instanceof HASMANY || resourceField instanceof BELONGSTOMANY) { const [model]: Model[] = await associated[getAccessor]({ ...options, where: { @@ -219,7 +225,7 @@ export async function update(ctx: Context, next: Next) { throw new Error(`${associatedName} associated model invalid`); } const { get: getAccessor } = resourceField.getAccessors(); - if (resourceField instanceof HasOne || resourceField instanceof BelongsTo) { + if (resourceField instanceof HASONE || resourceField instanceof BELONGSTO) { let model: Model = await associated[getAccessor]({ transaction, context: ctx }); if (model) { // @ts-ignore @@ -227,7 +233,7 @@ export async function update(ctx: Context, next: Next) { await model.updateAssociations(values, { transaction, context: ctx }); ctx.body = model; } - } else if (resourceField instanceof HasMany || resourceField instanceof BelongsToMany) { + } else if (resourceField instanceof HASMANY || resourceField instanceof BELONGSTOMANY) { const TargetModel = ctx.db.getModel(resourceField.getTarget()); const [model]: Model[] = await associated[getAccessor]({ where: { @@ -237,7 +243,7 @@ export async function update(ctx: Context, next: Next) { context: ctx, }); - if (resourceField instanceof BelongsToMany) { + if (resourceField instanceof BELONGSTOMANY) { const throughName = resourceField.getThroughName(); if (typeof values[throughName] === 'object') { const ThroughModel = resourceField.getThroughModel(); @@ -315,12 +321,12 @@ export async function destroy(ctx: Context, next: Next) { const {get: getAccessor, remove: removeAccessor, set: setAccessor} = resourceField.getAccessors(); const TargetModel = ctx.db.getModel(resourceField.getTarget()); const { where } = TargetModel.parseApiJson({ filter, context: ctx }); - if (resourceField instanceof HasOne || resourceField instanceof BelongsTo) { + if (resourceField instanceof HASONE || resourceField instanceof BELONGSTO) { const model: Model = await associated[getAccessor](commonOptions); await associated[setAccessor](null, commonOptions); // @ts-ignore ctx.body = await model.destroy(commonOptions); - } else if (resourceField instanceof HasMany || resourceField instanceof BelongsToMany) { + } else if (resourceField instanceof HASMANY || resourceField instanceof BELONGSTOMANY) { const primaryKey = resourceKeyAttribute || resourceField.options.targetKey || TargetModel.primaryKeyAttribute; const models: Model[] = await associated[getAccessor]({ where: resourceKey ? { [primaryKey]: resourceKey } : where, @@ -371,11 +377,11 @@ export async function sort(ctx: Context, next: Next) { } = ctx.action.params; if (associated && resourceField) { - if (resourceField instanceof HasOne || resourceField instanceof BelongsTo) { + if (resourceField instanceof HASONE || resourceField instanceof BELONGSTO) { throw new Error(`the association (${resourceName} belongs to ${associatedName}) cannot be sorted`); } // TODO(feature) - if (resourceField instanceof BelongsToMany) { + if (resourceField instanceof BELONGSTOMANY) { throw new Error('sorting for belongs to many association has not been implemented'); } } @@ -406,7 +412,7 @@ export async function sort(ctx: Context, next: Next) { const transaction = await ctx.db.sequelize.transaction(); const { where = {} } = Model.parseApiJson({ filter }); - if (associated && resourceField instanceof HasMany) { + if (associated && resourceField instanceof HASMANY) { where[resourceField.options.foreignKey] = associatedKey; } diff --git a/packages/actions/src/middlewares/associated.ts b/packages/actions/src/middlewares/associated.ts index 6407750e5..de825ed9f 100644 --- a/packages/actions/src/middlewares/associated.ts +++ b/packages/actions/src/middlewares/associated.ts @@ -1,6 +1,6 @@ -import { Context, Next, associate } from '../actions'; +import { Context, Next } from '../actions'; import { Action } from '@nocobase/resourcer'; -import { HasOne, HasMany, BelongsTo, BelongsToMany, Model } from '@nocobase/database'; +import { HASONE, HASMANY, BELONGSTO, BELONGSTOMANY } from '@nocobase/database'; export async function associated(ctx: Context, next: Next) { if (!(ctx.action instanceof Action)) { @@ -23,12 +23,12 @@ export async function associated(ctx: Context, next: Next) { let key: string; switch (true) { - case field instanceof BelongsTo: + case field instanceof BELONGSTO: key = field.options.targetKey || Model.primaryKeyAttribute; break; - case field instanceof HasOne: - case field instanceof HasMany: - case field instanceof BelongsToMany: + case field instanceof HASONE: + case field instanceof HASMANY: + case field instanceof BELONGSTOMANY: key = field.options.sourceKey; break; } diff --git a/packages/database/src/__tests__/associations.test.ts b/packages/database/src/__tests__/associations.test.ts index 25a7731a2..f1cc0a5c7 100644 --- a/packages/database/src/__tests__/associations.test.ts +++ b/packages/database/src/__tests__/associations.test.ts @@ -1,6 +1,10 @@ import { getDatabase } from './'; import { - HasMany, HasOne, Integer, BelongsTo, BelongsToMany + HASMANY as HasMany, + HASONE as HasOne, + INTEGER as Integer, + BELONGSTO as BelongsTo, + BELONGSTOMANY as BelongsToMany, } from '../fields'; import { DataTypes } from 'sequelize'; import Database from '..'; diff --git a/packages/database/src/__tests__/hooks.test.ts b/packages/database/src/__tests__/hooks.test.ts index ce9c80cdd..927b3b6ec 100644 --- a/packages/database/src/__tests__/hooks.test.ts +++ b/packages/database/src/__tests__/hooks.test.ts @@ -2,9 +2,9 @@ import { getDatabase } from './'; import Database from '../database'; import Table from '../table'; import Model from '../model'; -import { FieldContext, Integer, registerField } from '../fields'; +import { FieldContext, INTEGER, registerField } from '../fields'; -class TestHookField extends Integer { +class TestHookField extends INTEGER { constructor(options: any, context: FieldContext) { super(options, context); const Model = context.sourceTable.getModel(); @@ -157,4 +157,58 @@ describe('hooks', () => { const test = await Test3.create({}); expect(test.get('arr')).toEqual([ 1, 3, 2, 4 ]); }); + + it.only('add hook in custom field', async () => { + const table = db.table({ + name: 'test3', + fields: [ + { + type: 'json', + name: 'arr', + defaultValue: [], + }, + ], + hooks: { + beforeCreate(model) { + model.set('name', 'beforeCreate1'); + const arr = model.get('arr') as any[]; + arr.push(3); + model.set('arr', arr); + console.log('beforeCreate in table'); + }, + afterCreate(model) { + model.set('name', 'afterCreate1'); + const arr = model.get('arr') as any[]; + arr.push(4); + model.set('arr', arr); + console.log('afterCreate in table'); + }, + }, + }); + table.addField({ + type: 'string', + name: 'title', + }); + // await table.sync(); + let Test3 = db.getModel('test3'); + Test3.beforeCreate((model) => { + const arr = model.get('arr') as any[]; + arr.push(5); + model.set('arr', arr); + }); + Test3.afterCreate((model) => { + const arr = model.get('arr') as any[]; + arr.push(6); + model.set('arr', arr); + }); + // 通过 table 新增 + table.addField({ + type: 'testHook', + name: 'testHook', + }); + await table.sync(); + Test3 = db.getModel('test3'); + const test = await Test3.create({}); + expect(test.get('arr')).toEqual([ 3, 5, 1, 4, 6, 2 ]); + }); }); diff --git a/packages/database/src/__tests__/types.test.ts b/packages/database/src/__tests__/types.test.ts index 874699d03..32855e03e 100644 --- a/packages/database/src/__tests__/types.test.ts +++ b/packages/database/src/__tests__/types.test.ts @@ -1,25 +1,25 @@ import { buildField, - Boolean, - Integer, - String, - Text, - HasOne, - HasMany, - BelongsTo, - BelongsToMany, - Float, - Double, - Real, - Decimal, Column, - Time, - Date, - DateOnly, - Array, - Json, - Jsonb, - Password + BOOLEAN as Boolean, + INTEGER as Integer, + STRING as String, + TEXT as Text, + HASONE as HasOne, + HASMANY as HasMany, + BELONGSTO as BelongsTo, + BELONGSTOMANY as BelongsToMany, + FLOAT as Float, + DOUBLE as Double, + REAL as Real, + DECIMAL as Decimal, + TIME as Time, + DATE as Date, + DATEONLY as DateOnly, + ARRAY as Array, + JSON as Json, + JSONB as Jsonb, + PASSWORD as Password, } from '../fields'; import { DataTypes } from 'sequelize'; import { ABSTRACT } from 'sequelize/lib/data-types'; diff --git a/packages/database/src/fields/field-types.ts b/packages/database/src/fields/field-types.ts index 99e6b1722..e8b3f8ec8 100644 --- a/packages/database/src/fields/field-types.ts +++ b/packages/database/src/fields/field-types.ts @@ -81,13 +81,13 @@ export class Column extends Field { } } -export class Boolean extends Column { +export class BOOLEAN extends Column { } -export class Number extends Column { +export class NUMBER extends Column { } -export class Integer extends Number { +export class INTEGER extends NUMBER { public readonly options: Options.IntegerOptions; @@ -117,7 +117,7 @@ export class Integer extends Number { } } -export class Float extends Number { +export class FLOAT extends NUMBER { public readonly options: Options.FloatOptions; @@ -130,7 +130,7 @@ export class Float extends Number { } } -export class Double extends Number { +export class DOUBLE extends NUMBER { public readonly options: Options.DoubleOptions; public getAttributeOptions() { @@ -142,7 +142,7 @@ export class Double extends Number { } } -export class Decimal extends Number { +export class DECIMAL extends NUMBER { public readonly options: Options.DecimalOptions; @@ -155,7 +155,7 @@ export class Decimal extends Number { } } -export class Real extends Number { +export class REAL extends NUMBER { public readonly options: Options.RealOptions; @@ -168,7 +168,7 @@ export class Real extends Number { } } -export class String extends Column { +export class STRING extends Column { public readonly options: Options.StringOptions; @@ -182,7 +182,7 @@ export class String extends Column { } } -export class Text extends Column { +export class TEXT extends Column { public readonly options: Options.TextOptions; @@ -206,10 +206,10 @@ export class Text extends Column { } } -export class Time extends Column { +export class TIME extends Column { } -export class Date extends Column { +export class DATE extends Column { public readonly options: Options.DateOptions; @@ -223,13 +223,13 @@ export class Date extends Column { } } -export class DateOnly extends Column { +export class DATEONLY extends Column { } -export class Virtual extends Column { +export class VIRTUAL extends Column { } -export class Reference extends Virtual { +export class REFERENCE extends VIRTUAL { public getDataType() { return DataTypes.VIRTUAL; @@ -248,7 +248,7 @@ export class Reference extends Virtual { } } -export class Formula extends Virtual { +export class FORMULA extends VIRTUAL { public getDataType() { return DataTypes.VIRTUAL; @@ -284,7 +284,7 @@ export class Formula extends Virtual { } } -export class Password extends String { +export class PASSWORD extends STRING { public getDataType() { return DataTypes.STRING; @@ -307,7 +307,7 @@ export class Password extends String { } } -export class Array extends Column { +export class ARRAY extends Column { public readonly options: Options.ArrayOptions; @@ -324,10 +324,10 @@ export class Array extends Column { } } -export class Json extends Column { +export class JSON extends Column { } -export class Jsonb extends Column { +export class JSONB extends Column { } export interface HasOneAccessors { @@ -371,16 +371,16 @@ export interface BelongsToManyAccessors { export abstract class Relation extends Field { public getAssociationType() { - if (this instanceof HasOne) { + if (this instanceof HASONE) { return 'hasOne'; } - if (this instanceof HasMany) { + if (this instanceof HASMANY) { return 'hasMany'; } - if (this instanceof BelongsTo) { + if (this instanceof BELONGSTO) { return 'belongsTo'; } - if (this instanceof BelongsToMany) { + if (this instanceof BELONGSTOMANY) { return 'belongsToMany'; } } @@ -390,10 +390,10 @@ export abstract class Relation extends Field { if (target) { return target; } - if (this instanceof HasMany) { + if (this instanceof HASMANY) { return name; } - if (this instanceof BelongsToMany) { + if (this instanceof BELONGSTOMANY) { return name; } return Utils.pluralize(name); @@ -462,7 +462,7 @@ class HasOneOrMany extends Relation { } } -export class HasOne extends HasOneOrMany { +export class HASONE extends HasOneOrMany { public readonly options: Options.HasOneOptions; @@ -489,7 +489,7 @@ export class HasOne extends HasOneOrMany { } } -export class HasMany extends HasOneOrMany { +export class HASMANY extends HasOneOrMany { public readonly options: Options.HasManyOptions; @@ -512,7 +512,7 @@ export class HasMany extends HasOneOrMany { } } -export class BelongsTo extends Relation { +export class BELONGSTO extends Relation { public readonly options: Options.BelongsToOptions; @@ -567,7 +567,7 @@ export class BelongsTo extends Relation { } } -export class BelongsToMany extends Relation { +export class BELONGSTOMANY extends Relation { public readonly options: Options.BelongsToManyOptions; diff --git a/packages/database/src/fields/index.ts b/packages/database/src/fields/index.ts index 19334075e..60d45b777 100644 --- a/packages/database/src/fields/index.ts +++ b/packages/database/src/fields/index.ts @@ -110,15 +110,14 @@ export function buildField(options: FieldOptions, context: Fields.FieldContext) registerFields({ ...Fields, // aliases - Int: Fields.Integer, - TinyInt: Fields.Integer, - TinyInteger: Fields.Integer, - SmallInt: Fields.Integer, - SmallInteger: Fields.Integer, - MediumInt: Fields.Integer, - MediumInteger: Fields.Integer, - BigInt: Fields.Integer, - BigInteger: Fields.Integer, - Timestamp: Fields.Date, - Creator: Fields.BelongsTo, + INT: Fields.INTEGER, + TINYINT: Fields.INTEGER, + TINYINTEGER: Fields.INTEGER, + SMALLINT: Fields.INTEGER, + SMALLINTEGER: Fields.INTEGER, + MEDIUMINT: Fields.INTEGER, + MEDIUMINTEGER: Fields.INTEGER, + BIGINT: Fields.INTEGER, + BIGINTEGER: Fields.INTEGER, + TIMESTAMP: Fields.DATE, }); diff --git a/packages/database/src/model.ts b/packages/database/src/model.ts index 09e30a24e..f8856207c 100644 --- a/packages/database/src/model.ts +++ b/packages/database/src/model.ts @@ -2,7 +2,13 @@ import { Model as SequelizeModel, Op, Sequelize, ProjectionAlias, Utils, SaveOptions, } from 'sequelize'; import Database from './database'; -import { HasOne, HasMany, BelongsTo, BelongsToMany, getDataTypeKey } from './fields'; +import { + getDataTypeKey, + HASONE, + HASMANY, + BELONGSTO, + BELONGSTOMANY, +} from './fields'; import { toInclude } from './utils'; export interface ApiJsonOptions { @@ -269,7 +275,7 @@ export abstract class Model extends SequelizeModel { await this[accessors.set](data, opts); } else if (typeof data === 'object') { const Target = association.getTargetModel(); - const targetAttribute = association instanceof BelongsTo + const targetAttribute = association instanceof BELONGSTO ? association.options.targetKey : association.options.sourceKey; if (data[targetAttribute]) { @@ -407,7 +413,7 @@ export abstract class Model extends SequelizeModel { } } - if (association instanceof BelongsToMany) { + if (association instanceof BELONGSTOMANY) { belongsToManyList.push({ item, target @@ -424,8 +430,8 @@ export abstract class Model extends SequelizeModel { // 后处理 belongsToMany 的更新内容 if (belongsToManyList.length) { - const ThroughModel = (association as BelongsToMany).getThroughModel(); - const throughName = (association as BelongsToMany).getThroughName(); + const ThroughModel = (association as BELONGSTOMANY).getThroughModel(); + const throughName = (association as BELONGSTOMANY).getThroughName(); for (const { item, target } of belongsToManyList) { const throughValues = item[throughName]; @@ -453,11 +459,11 @@ export abstract class Model extends SequelizeModel { const table = this.database.getTable(this.constructor.name); const association = table.getAssociations().get(key); switch (true) { - case association instanceof BelongsTo: - case association instanceof HasOne: + case association instanceof BELONGSTO: + case association instanceof HASONE: return this.updateSingleAssociation(key, data, options); - case association instanceof HasMany: - case association instanceof BelongsToMany: + case association instanceof HASMANY: + case association instanceof BELONGSTOMANY: return this.updateMultipleAssociation(key, data, options); } } diff --git a/packages/database/src/table.ts b/packages/database/src/table.ts index 75f858f53..92549ede1 100644 --- a/packages/database/src/table.ts +++ b/packages/database/src/table.ts @@ -10,8 +10,8 @@ import { buildField, FieldOptions, Relation, - BelongsTo, - BelongsToMany, + BELONGSTO, + BELONGSTOMANY, } from './fields'; import Database from './database'; import { Model, ModelCtor } from './model'; @@ -166,7 +166,7 @@ export class Table { if (this.database.isDefined(target)) { const TargetModel = this.database.getModel(target); // 如果关系表在之后才定义,未设置 targetKey 时,targetKey 默认值需要在 target model 初始化之后才能取到 - if (association instanceof BelongsTo || association instanceof BelongsToMany) { + if (association instanceof BELONGSTO || association instanceof BELONGSTOMANY) { association.updateOptionsAfterTargetModelBeDefined(); } this.Model[type](TargetModel, association.getAssociationOptions()); @@ -235,7 +235,7 @@ export class Table { for (const association of this.associations.values()) { const target = association.getTarget(); names.add(target); - if (association instanceof BelongsToMany) { + if (association instanceof BELONGSTOMANY) { names.add(association.getThroughName()); } } diff --git a/packages/plugin-users/src/actions/login.ts b/packages/plugin-users/src/actions/login.ts index 3864e3ec6..0c8fe8c8e 100644 --- a/packages/plugin-users/src/actions/login.ts +++ b/packages/plugin-users/src/actions/login.ts @@ -1,6 +1,6 @@ import { actions } from '@nocobase/actions'; +import { PASSWORD } from '@nocobase/database'; import cryptoRandomString from 'crypto-random-string'; -import { Password } from "@nocobase/database"; export default async (ctx: actions.Context, next: actions.Next) => { const { values } = ctx.action.params; @@ -14,7 +14,7 @@ export default async (ctx: actions.Context, next: actions.Next) => { if (!user) { ctx.throw(401, 'Unauthorized'); } - if (!Password.verify(values.password, user.password)) { + if (!PASSWORD.verify(values.password, user.password)) { ctx.throw(401, 'Unauthorized'); } if (!user.token) { diff --git a/packages/server/src/middleware.ts b/packages/server/src/middleware.ts index a62489b09..3a7bbc1da 100644 --- a/packages/server/src/middleware.ts +++ b/packages/server/src/middleware.ts @@ -2,7 +2,7 @@ import qs from 'qs'; import compose from 'koa-compose'; import { pathToRegexp } from 'path-to-regexp'; import Resourcer, { getNameByParams, KoaMiddlewareOptions, parseRequest, ResourcerContext } from '@nocobase/resourcer'; -import Database, { BelongsTo, BelongsToMany, HasMany, HasOne } from '@nocobase/database'; +import Database, { BELONGSTO, BELONGSTOMANY, HASMANY, HASONE } from '@nocobase/database'; interface MiddlewareOptions extends KoaMiddlewareOptions { resourcer?: Resourcer; @@ -49,18 +49,18 @@ export function middleware(options: MiddlewareOptions = {}) { } if (database.isDefined(tableName)) { const table = database.getTable(tableName); - const field = table.getField(names[0]) as BelongsTo | HasMany | BelongsToMany | HasOne; + const field = table.getField(names[0]) as BELONGSTO | HASMANY | BELONGSTOMANY | HASONE; if (names.length == 0 || field) { let resourceType = 'single'; let actions = {}; if (field) { - if (field instanceof HasOne) { + if (field instanceof HASONE) { resourceType = 'hasOne'; - } else if (field instanceof HasMany) { + } else if (field instanceof HASMANY) { resourceType = 'hasMany'; - } else if (field instanceof BelongsTo) { + } else if (field instanceof BELONGSTO) { resourceType = 'belongsTo'; - } else if (field instanceof BelongsToMany) { + } else if (field instanceof BELONGSTOMANY) { resourceType = 'belongsToMany'; } if (field.options.actions) {