refactor(database): model toJSON without the hidden fields

This commit is contained in:
chenos 2022-02-23 18:18:38 +08:00
parent e140227798
commit 872ee79146
20 changed files with 135 additions and 68 deletions

View File

@ -1,9 +1,10 @@
import merge from 'deepmerge';
import { EventEmitter } from 'events';
import { default as lodash, default as _ } from 'lodash';
import { Model, ModelCtor, ModelOptions, SyncOptions } from 'sequelize';
import { ModelCtor, ModelOptions, SyncOptions } from 'sequelize';
import { Database } from './database';
import { Field, FieldOptions } from './fields';
import { Model } from './model';
import { Repository } from './repository';
export type RepositoryType = typeof Repository;
@ -41,7 +42,7 @@ export class Collection<
context: CollectionContext;
isThrough?: boolean;
fields: Map<string, any> = new Map<string, any>();
model: ModelCtor<Model<TModelAttributes, TCreationAttributes>>;
model: ModelCtor<Model>;
repository: Repository<TModelAttributes, TCreationAttributes>;
get filterTargetKey() {
@ -81,12 +82,16 @@ export class Collection<
return;
}
const { name, model, autoGenId = true } = this.options;
let M = Model;
let M: ModelCtor<Model> = Model;
if (this.context.database.sequelize.isDefined(name)) {
const m = this.context.database.sequelize.model(name);
if ((m as any).isThrough) {
// @ts-ignore
this.model = m;
Object.defineProperty(this.model, 'database', { value: this.context.database });
// @ts-ignore
this.model.database = this.context.database;
// @ts-ignore
this.model.collection = this;
return;
}
}
@ -95,6 +100,7 @@ export class Collection<
} else if (model) {
M = model;
}
// @ts-ignore
this.model = class extends M {};
this.model.init(null, this.sequelizeModelOptions());
@ -102,7 +108,10 @@ export class Collection<
this.model.removeAttribute('id');
}
Object.defineProperty(this.model, 'database', { value: this.context.database });
// @ts-ignore
this.model.database = this.context.database;
// @ts-ignore
this.model.collection = this;
}
setRepository(repository?: RepositoryType | string) {

View File

@ -2,7 +2,6 @@ import { applyMixins, AsyncEmitter } from '@nocobase/utils';
import merge from 'deepmerge';
import { EventEmitter } from 'events';
import {
Model,
ModelCtor,
Op,
Options,
@ -15,6 +14,7 @@ import { Collection, CollectionOptions, RepositoryType } from './collection';
import { ImporterReader, ImportFileExtension } from './collection-importer';
import * as FieldTypes from './fields';
import { Field, FieldContext, RelationField } from './fields';
import { Model } from './model';
import { ModelHook } from './model-hook';
import extendOperators from './operators';
import { RelationRepository } from './relation-repository/relation-repository';
@ -53,7 +53,7 @@ export class Database extends EventEmitter implements AsyncEmitter {
sequelize: Sequelize;
fieldTypes = new Map();
options: IDatabaseOptions;
models = new Map<string, ModelCtor<any>>();
models = new Map<string, ModelCtor<Model>>();
repositories = new Map<string, RepositoryType>();
operators = new Map();
collections = new Map<string, Collection>();

View File

@ -1,5 +1,6 @@
import { lodash } from '@umijs/utils';
import { DataTypes, Model } from 'sequelize';
import { DataTypes } from 'sequelize';
import { Model } from '../model';
import { BaseColumnFieldOptions, Field } from './field';
export class ContextField extends Field {

View File

@ -1,9 +1,9 @@
import { Model, ModelCtor } from 'sequelize';
import _ from 'lodash';
import { flatten, unflatten } from 'flat';
import { Database } from './database';
import lodash from 'lodash';
import { flatten } from 'flat';
import { default as lodash, default as _ } from 'lodash';
import { ModelCtor } from 'sequelize';
import { Collection } from './collection';
import { Database } from './database';
import { Model } from './model';
const debug = require('debug')('noco-database');

View File

@ -1,10 +1,11 @@
export { Model, ModelCtor, SyncOptions } from 'sequelize';
export { ModelCtor, SyncOptions } from 'sequelize';
export * from './collection';
export * from './database';
export { Database as default } from './database';
export * from './fields';
export * from './magic-attribute-model';
export * from './mock-database';
export * from './model';
export * from './relation-repository/belongs-to-many-repository';
export * from './relation-repository/belongs-to-repository';
export * from './relation-repository/hasmany-repository';

View File

@ -1,7 +1,7 @@
import { Model } from 'sequelize';
import { merge } from '@nocobase/utils';
import _ from 'lodash';
import Database from './database';
import { Model } from './model';
export class MagicAttributeModel extends Model {
get magicAttribute() {

View File

@ -1,7 +1,7 @@
import lodash from 'lodash';
import { Model } from 'sequelize';
import type { SequelizeHooks } from 'sequelize/types/lib/hooks';
import Database from './database';
import { Model } from './model';
const { hooks } = require('sequelize/lib/hooks');

View File

@ -0,0 +1,59 @@
import { Model as SequelizeModel } from 'sequelize';
import { Collection } from './collection';
import { Database } from './database';
interface IModel {
[key: string]: any;
}
export class Model<TModelAttributes extends {} = any, TCreationAttributes extends {} = TModelAttributes>
extends SequelizeModel<TModelAttributes, TCreationAttributes>
implements IModel
{
public static database: Database;
public static collection: Collection;
// [key: string]: any;
private toJsonWithoutHiddenFields(data, { model, collection }): any {
if (!data) {
return data;
}
const db = (this.constructor as any).database as Database;
const hidden = [];
collection.forEachField((field) => {
if (field.options.hidden) {
hidden.push(field.options.name);
}
});
const json = {};
Object.keys(data).forEach((key) => {
if (hidden.includes(key)) {
return;
}
if (model.hasAlias(key)) {
const association = model.associations[key];
const opts = {
model: association.target,
collection: db.getCollection(association.target.name),
};
if (['HasMany', 'BelongsToMany'].includes(association.associationType)) {
if (Array.isArray(data[key])) {
json[key] = data[key].map((item) => this.toJsonWithoutHiddenFields(item, opts));
}
} else {
json[key] = this.toJsonWithoutHiddenFields(data[key], opts);
}
} else {
json[key] = data[key];
}
});
return json;
}
public toJSON<T extends TModelAttributes>(): T {
return this.toJsonWithoutHiddenFields(super.toJSON(), {
model: this.constructor,
collection: (this.constructor as any).collection,
});
}
}

View File

@ -1,10 +1,11 @@
import { BelongsToMany, Model, Op, Transaction } from 'sequelize';
import lodash from 'lodash';
import { BelongsToMany, Op, Transaction } from 'sequelize';
import { Model } from '../model';
import { CreateOptions, DestroyOptions, FindOptions, TargetKey, UpdateOptions } from '../repository';
import { updateThroughTableValue } from '../update-associations';
import { FindAndCountOptions, FindOneOptions, MultipleRelationRepository } from './multiple-relation-repository';
import { CreateOptions, DestroyOptions, FindOptions, TargetKey, UpdateOptions } from '../repository';
import { AssociatedOptions, PrimaryKeyWithThroughValues } from './types';
import lodash from 'lodash';
import { transaction } from './relation-repository';
import { AssociatedOptions, PrimaryKeyWithThroughValues } from './types';
type CreateBelongsToManyOptions = CreateOptions;

View File

@ -1,7 +1,6 @@
import { Model } from 'sequelize';
import { SingleRelationFindOption, SingleRelationRepository } from './single-relation-repository';
import { Model } from '../model';
import { CreateOptions, UpdateOptions } from '../repository';
import { SingleRelationFindOption, SingleRelationRepository } from './single-relation-repository';
interface BelongsToFindOptions extends SingleRelationFindOption {}

View File

@ -1,14 +1,14 @@
import { BelongsToMany, HasMany, Model, Op, Sequelize } from 'sequelize';
import { omit } from 'lodash';
import { HasMany, Op } from 'sequelize';
import { Model } from '../model';
import { CreateOptions, DestroyOptions, FindOptions, TargetKey, TK, UpdateOptions } from '../repository';
import {
AssociatedOptions,
FindAndCountOptions,
FindOneOptions,
MultipleRelationRepository,
MultipleRelationRepository
} from './multiple-relation-repository';
import { CreateOptions, DestroyOptions, FindOptions, TK, TargetKey, UpdateOptions } from '../repository';
import { transaction } from './relation-repository';
import lodash, { omit } from 'lodash';
interface IHasManyRepository<M extends Model> {
find(options?: FindOptions): Promise<M>;

View File

@ -1,6 +1,6 @@
import { Model } from 'sequelize';
import { SingleRelationFindOption, SingleRelationRepository } from './single-relation-repository';
import { Model } from '../model';
import { CreateOptions } from '../repository';
import { SingleRelationFindOption, SingleRelationRepository } from './single-relation-repository';
interface HasOneFindOptions extends SingleRelationFindOption {}

View File

@ -1,14 +1,15 @@
import { Association, BelongsTo, BelongsToMany, HasMany, HasOne, Model, ModelCtor, Transaction } from 'sequelize';
import { OptionsParser } from '../options-parser';
import { Collection } from '../collection';
import { CreateOptions, Filter, FindOptions } from '../repository';
import FilterParser from '../filter-parser';
import { UpdateGuard } from '../update-guard';
import { updateAssociations } from '../update-associations';
import lodash from 'lodash';
import { transactionWrapperBuilder } from '../transaction-decorator';
import { RelationField } from '../fields/relation-field';
import { Association, BelongsTo, BelongsToMany, HasMany, HasOne, ModelCtor, Transaction } from 'sequelize';
import { Collection } from '../collection';
import Database from '../database';
import { RelationField } from '../fields/relation-field';
import FilterParser from '../filter-parser';
import { Model } from '../model';
import { OptionsParser } from '../options-parser';
import { CreateOptions, Filter, FindOptions } from '../repository';
import { transactionWrapperBuilder } from '../transaction-decorator';
import { updateAssociations } from '../update-associations';
import { UpdateGuard } from '../update-guard';
export const transaction = transactionWrapperBuilder(function () {
return this.sourceCollection.model.sequelize.transaction();

View File

@ -1,8 +1,9 @@
import { RelationRepository, transaction } from './relation-repository';
import { Model, SingleAssociationAccessors } from 'sequelize';
import { updateModelByValues } from '../update-associations';
import lodash from 'lodash';
import { SingleAssociationAccessors } from 'sequelize';
import { Model } from '../model';
import { Appends, Except, Fields, Filter, TargetKey, TransactionAble, UpdateOptions } from '../repository';
import { updateModelByValues } from '../update-associations';
import { RelationRepository, transaction } from './relation-repository';
export interface SingleRelationFindOption extends TransactionAble {
fields?: Fields;

View File

@ -6,16 +6,16 @@ import {
DestroyOptions as SequelizeDestroyOptions,
FindAndCountOptions as SequelizeAndCountOptions,
FindOptions as SequelizeFindOptions,
Model,
ModelCtor,
Op,
Transaction,
UpdateOptions as SequelizeUpdateOptions,
UpdateOptions as SequelizeUpdateOptions
} from 'sequelize';
import { Collection } from './collection';
import { Database } from './database';
import { RelationField } from './fields';
import FilterParser from './filter-parser';
import { Model } from './model';
import { OptionsParser } from './options-parser';
import { BelongsToManyRepository } from './relation-repository/belongs-to-many-repository';
import { BelongsToRepository } from './relation-repository/belongs-to-repository';

View File

@ -5,10 +5,10 @@ import {
HasMany,
HasOne,
Hookable,
Model,
ModelCtor,
Transactionable,
Transactionable
} from 'sequelize';
import { Model } from './model';
import { TransactionAble } from './repository';
import { UpdateGuard } from './update-guard';
@ -257,11 +257,11 @@ export async function updateSingleAssociation(
let dataKey: string;
let M: ModelCtor<Model>;
if (association.associationType === 'BelongsTo') {
M = association.target;
M = association.target as ModelCtor<Model>;
// @ts-ignore
dataKey = association.targetKey;
} else {
M = association.source;
M = association.source as ModelCtor<Model>;
dataKey = M.primaryKeyAttribute;
}
@ -396,7 +396,7 @@ export async function updateMultipleAssociation(
list3.push(instance);
} else {
// set & update record
const instance = await association.target.findByPk(item[pk], {
const instance = await association.target.findByPk<any>(item[pk], {
transaction,
});
const addAccessor = association.accessors.add;

View File

@ -1,8 +1,8 @@
import lodash from 'lodash';
import { Model, ModelCtor } from 'sequelize';
import { ModelCtor } from 'sequelize';
import { Model } from './model';
import { AssociationKeysToBeUpdate, BlackList, WhiteList } from './repository';
type UpdateValueItem = string | number | UpdateValues;
type UpdateValues = {

View File

@ -19,7 +19,7 @@ export async function signin(ctx: Context, next: Next) {
ctx.throw(401, '请填写邮箱账号');
}
const User = ctx.db.getCollection('users');
const user = await User.model.scope('withPassword').findOne<any>({
const user = await User.model.findOne<any>({
where: {
[uniqueField]: values[uniqueField],
},
@ -36,8 +36,10 @@ export async function signin(ctx: Context, next: Next) {
user.token = crypto.randomBytes(20).toString('hex');
await user.save();
}
ctx.body = user.toJSON();
delete ctx.body.password;
ctx.body = {
...user.toJSON(),
token: user.get('token'),
};
await next();
}
@ -47,10 +49,10 @@ export async function signout(ctx: Context, next: Next) {
}
export async function signup(ctx: Context, next: Next) {
const User = ctx.db.getCollection('users');
const User = ctx.db.getRepository('users');
const { values } = ctx.action.params;
try {
const user = await User.model.create(values);
const user = await User.create({ values });
ctx.body = user;
} catch (error) {
if (error.errors) {
@ -139,7 +141,7 @@ export async function changePassword(ctx: Context, next: Next) {
ctx.throw(401, 'Unauthorized');
}
const User = ctx.db.getCollection('users');
const user = await User.model.scope('withPassword').findOne<any>({
const user = await User.model.findOne<any>({
where: {
email: ctx.state.currentUser.email,
},

View File

@ -4,14 +4,6 @@ export default {
name: 'users',
title: '{{t("Users")}}',
sortable: 'sort',
scopes: {
withPassword: {
attributes: { include: ['password'] },
},
},
defaultScope: {
attributes: { exclude: ['password'] },
},
fields: [
{
interface: 'string',
@ -39,6 +31,7 @@ export default {
interface: 'password',
type: 'password',
name: 'password',
hidden: true,
uiSchema: {
type: 'string',
title: '{{t("Password")}}',

View File

@ -1,10 +1,10 @@
import glob from 'glob';
import compose from 'koa-compose';
import _ from 'lodash';
import { pathToRegexp } from 'path-to-regexp';
import Action, { ActionName } from './action';
import Resource, { ResourceOptions } from './resource';
import { parseRequest, getNameByParams, ParsedParams, requireModule, parseQuery } from './utils';
import { pathToRegexp } from 'path-to-regexp';
import _ from 'lodash';
import { getNameByParams, ParsedParams, parseQuery, parseRequest, requireModule } from './utils';
export interface ResourcerContext {
resourcer?: Resourcer;