From 16d07e9b4aa39bf775d4aa2b9aae18852e1dd51a Mon Sep 17 00:00:00 2001 From: chenos Date: Fri, 11 Feb 2022 14:34:33 +0800 Subject: [PATCH] feat: add sortable parameter to collection options --- .../src/__tests__/collection.sortable.test.ts | 60 +++++++++++++++++++ packages/database/src/collection.ts | 36 +++++++++-- packages/database/src/database.ts | 23 ++++++- packages/database/src/repository.ts | 29 ++++----- 4 files changed, 124 insertions(+), 24 deletions(-) create mode 100644 packages/database/src/__tests__/collection.sortable.test.ts diff --git a/packages/database/src/__tests__/collection.sortable.test.ts b/packages/database/src/__tests__/collection.sortable.test.ts new file mode 100644 index 000000000..26097302b --- /dev/null +++ b/packages/database/src/__tests__/collection.sortable.test.ts @@ -0,0 +1,60 @@ +import { mockDatabase } from './index'; + +describe('collection sortable options', () => { + test('sortable=true', async () => { + const db = mockDatabase(); + + const Test = db.collection({ + name: 'test', + sortable: true, + }); + + const model = Test.model; + + await db.sync(); + const instance = await model.create(); + expect(model.rawAttributes['sort']).toBeDefined(); + expect(instance.get('sort')).toBe(1); + }); + + test('sortable=string', async () => { + const db = mockDatabase(); + + const Test = db.collection({ + name: 'test', + sortable: 'order', + }); + + const model = Test.model; + + await db.sync(); + const instance = await model.create(); + expect(model.rawAttributes['order']).toBeDefined(); + expect(instance.get('order')).toBe(1); + }); + + test('sortable=object', async () => { + const db = mockDatabase(); + + const Test = db.collection({ + name: 'test', + sortable: { + name: 'sort', + scopeKey: 'status', + }, + fields: [{ type: 'string', name: 'status' }], + }); + + await db.sync(); + + const t1 = await Test.model.create({ status: 'publish' }); + const t2 = await Test.model.create({ status: 'publish' }); + const t3 = await Test.model.create({ status: 'draft' }); + const t4 = await Test.model.create({ status: 'draft' }); + + expect(t1.get('sort')).toBe(1); + expect(t2.get('sort')).toBe(2); + expect(t3.get('sort')).toBe(1); + expect(t4.get('sort')).toBe(2); + }); +}); diff --git a/packages/database/src/collection.ts b/packages/database/src/collection.ts index 3f8fbb64e..baa631b67 100644 --- a/packages/database/src/collection.ts +++ b/packages/database/src/collection.ts @@ -1,17 +1,18 @@ -import { Sequelize, ModelCtor, Model, ModelOptions } from 'sequelize'; +import merge from 'deepmerge'; import { EventEmitter } from 'events'; +import { default as lodash, default as _ } from 'lodash'; +import { Model, ModelCtor, ModelOptions } from 'sequelize'; +import { SyncOptions } from 'sequelize/types/lib/sequelize'; import { Database } from './database'; import { Field, FieldOptions } from './fields'; - -import _ from 'lodash'; import { Repository } from './repository'; -import { SyncOptions } from 'sequelize/types/lib/sequelize'; -import lodash from 'lodash'; -import merge from 'deepmerge'; + const { hooks } = require('sequelize/lib/hooks'); export type RepositoryType = typeof Repository; +export type CollectionSortable = string | boolean | { name?: string; scopeKey?: string }; + export interface CollectionOptions extends Omit { name: string; tableName?: string; @@ -19,6 +20,7 @@ export interface CollectionOptions extends Omit { fields?: FieldOptions[]; model?: string | ModelCtor; repository?: string | RepositoryType; + sortable?: CollectionSortable; /** * @default true */ @@ -61,6 +63,7 @@ export class Collection< this.modelInit(); this.setFields(options.fields); this.setRepository(options.repository); + this.setSortable(options.sortable); } private sequelizeModelOptions() { @@ -211,6 +214,27 @@ export class Collection< (this.model)._setupHooks(bindHooks); } + setSortable(sortable) { + if (!sortable) { + return; + } + if (sortable === true) { + this.setField('sort', { + type: 'sort', + hidden: true, + }); + } + if (typeof sortable === 'string') { + this.setField(sortable, { + type: 'sort', + hidden: true, + }); + } else if (typeof sortable === 'object') { + const { name, ...opts } = sortable; + this.setField(name || 'sort', { type: 'sort', hidden: true, ...opts }); + } + } + /** * TODO * diff --git a/packages/database/src/database.ts b/packages/database/src/database.ts index b7e613d03..b3beccefd 100644 --- a/packages/database/src/database.ts +++ b/packages/database/src/database.ts @@ -1,7 +1,16 @@ import { applyMixins, AsyncEmitter } from '@nocobase/utils'; import merge from 'deepmerge'; import { EventEmitter } from 'events'; -import { Model, ModelCtor, Op, Options, QueryInterfaceDropAllTablesOptions, Sequelize, SyncOptions, Utils } from 'sequelize'; +import { + Model, + ModelCtor, + Op, + Options, + QueryInterfaceDropAllTablesOptions, + Sequelize, + SyncOptions, + Utils +} from 'sequelize'; import { Collection, CollectionOptions, RepositoryType } from './collection'; import { ImporterReader, ImportFileExtension } from './collection-importer'; import * as FieldTypes from './fields'; @@ -295,6 +304,18 @@ export function extend(collectionOptions: CollectionOptions, mergeOptions?: Merg }; } +export const defineCollection = (collectionOptions: CollectionOptions) => { + return collectionOptions; +}; + +export const extendCollection = (collectionOptions: CollectionOptions, mergeOptions?: MergeOptions) => { + return { + collectionOptions, + mergeOptions, + extend: true, + }; +}; + applyMixins(Database, [AsyncEmitter]); export default Database; diff --git a/packages/database/src/repository.ts b/packages/database/src/repository.ts index 6f229fd5a..80870040f 100644 --- a/packages/database/src/repository.ts +++ b/packages/database/src/repository.ts @@ -1,31 +1,30 @@ +import lodash, { omit } from 'lodash'; import { Association, BulkCreateOptions, CreateOptions as SequelizeCreateOptions, - UpdateOptions as SequelizeUpdateOptions, - FindAndCountOptions as SequelizeAndCountOptions, DestroyOptions as SequelizeDestroyOptions, + FindAndCountOptions as SequelizeAndCountOptions, FindOptions as SequelizeFindOptions, Model, ModelCtor, Op, Transaction, + UpdateOptions as SequelizeUpdateOptions } from 'sequelize'; - import { Collection } from './collection'; -import lodash, { omit } from 'lodash'; import { Database } from './database'; -import { updateAssociations, updateModelByValues } from './update-associations'; import { RelationField } from './fields'; import FilterParser from './filter-parser'; import { OptionsParser } from './options-parser'; -import { RelationRepository } from './relation-repository/relation-repository'; -import { HasOneRepository } from './relation-repository/hasone-repository'; -import { BelongsToRepository } from './relation-repository/belongs-to-repository'; import { BelongsToManyRepository } from './relation-repository/belongs-to-many-repository'; +import { BelongsToRepository } from './relation-repository/belongs-to-repository'; import { HasManyRepository } from './relation-repository/hasmany-repository'; -import { UpdateGuard } from './update-guard'; +import { HasOneRepository } from './relation-repository/hasone-repository'; +import { RelationRepository } from './relation-repository/relation-repository'; import { transactionWrapperBuilder } from './transaction-decorator'; +import { updateAssociations, updateModelByValues } from './update-associations'; +import { UpdateGuard } from './update-guard'; const debug = require('debug')('noco-database'); @@ -324,15 +323,11 @@ export class Repository