From b8c85c91b4a76b64e5b67458d4aa1e753afadf9d Mon Sep 17 00:00:00 2001 From: ChengLei Shao Date: Sat, 6 May 2023 07:55:26 +0800 Subject: [PATCH] test: filter nested association (#1802) * test: filter nested association * chore: log * fix: tree instance to json * ci: test case * chore: test * chore: log --------- Co-authored-by: chenos --- .github/workflows/nocobase-test-lite.yml | 1 - packages/core/actions/src/actions/list.ts | 10 +- .../src/__tests__/repository/find.test.ts | 96 ++++++++++++- packages/core/database/src/repository.ts | 3 +- .../adjacency-list-repository.ts | 12 +- .../src/__tests__/http-api/find.test.ts | 128 ++++++++++++++++++ 6 files changed, 238 insertions(+), 12 deletions(-) create mode 100644 packages/plugins/collection-manager/src/__tests__/http-api/find.test.ts diff --git a/.github/workflows/nocobase-test-lite.yml b/.github/workflows/nocobase-test-lite.yml index 5aeb2e97d..2114d9365 100644 --- a/.github/workflows/nocobase-test-lite.yml +++ b/.github/workflows/nocobase-test-lite.yml @@ -68,7 +68,6 @@ jobs: node-version: ${{ matrix.node_version }} cache: 'yarn' - run: yarn install - # - run: yarn build - name: Test with postgres run: yarn nocobase install -f && yarn test env: diff --git a/packages/core/actions/src/actions/list.ts b/packages/core/actions/src/actions/list.ts index be006fb86..2920c832b 100644 --- a/packages/core/actions/src/actions/list.ts +++ b/packages/core/actions/src/actions/list.ts @@ -43,12 +43,20 @@ async function listWithPagination(ctx: Context) { const repository = getRepositoryFromParams(ctx); - const [rows, count] = await repository.findAndCount({ + const options = { context: ctx, ...findArgs(ctx), ...pageArgsToLimitArgs(parseInt(String(page)), parseInt(String(pageSize))), + }; + + Object.keys(options).forEach((key) => { + if (options[key] === undefined) { + delete options[key]; + } }); + const [rows, count] = await repository.findAndCount(options); + ctx.body = { count, rows, diff --git a/packages/core/database/src/__tests__/repository/find.test.ts b/packages/core/database/src/__tests__/repository/find.test.ts index c33bc79df..59abaa76f 100644 --- a/packages/core/database/src/__tests__/repository/find.test.ts +++ b/packages/core/database/src/__tests__/repository/find.test.ts @@ -1,7 +1,101 @@ import { mockDatabase } from '../index'; import Database from '@nocobase/database'; import { Collection } from '../../collection'; -import { OptionsParser } from '../../options-parser'; + +describe('find with associations', () => { + let db: Database; + beforeEach(async () => { + db = mockDatabase({ + tablePrefix: '', + }); + + await db.clean({ drop: true }); + }); + + afterEach(async () => { + await db.close(); + }); + + it('should filter by association field', async () => { + const User = db.collection({ + name: 'users', + tree: 'adjacency-list', + fields: [ + { type: 'string', name: 'name' }, + { type: 'hasMany', name: 'posts', target: 'posts', foreignKey: 'user_id' }, + { + type: 'belongsTo', + name: 'parent', + foreignKey: 'parent_id', + treeParent: true, + }, + { + type: 'hasMany', + name: 'children', + foreignKey: 'parent_id', + treeChildren: true, + }, + ], + }); + + const Post = db.collection({ + name: 'posts', + fields: [ + { type: 'string', name: 'title' }, + { type: 'belongsTo', name: 'user', target: 'users', foreignKey: 'user_id' }, + ], + }); + + await db.sync(); + + expect(User.options.tree).toBeTruthy(); + + await User.repository.create({ + values: [ + { + name: 'u1', + posts: [ + { + title: 'u1p1', + }, + ], + children: [ + { + name: 'u2', + posts: [ + { + title: '标题2', + }, + ], + }, + ], + }, + ], + }); + + const filter = { + $and: [ + { + children: { + posts: { + title: { + $eq: '标题2', + }, + }, + }, + }, + ], + }; + + const [findResult, count] = await User.repository.findAndCount({ + filter, + offset: 0, + limit: 20, + }); + + expect(findResult[0].get('name')).toEqual('u1'); + }); +}); describe('repository find', () => { let db: Database; diff --git a/packages/core/database/src/repository.ts b/packages/core/database/src/repository.ts index cf53ae34b..d49773255 100644 --- a/packages/core/database/src/repository.ts +++ b/packages/core/database/src/repository.ts @@ -351,10 +351,9 @@ export class Repository { - const transaction = await this.getTransaction(options); options = { ...options, - transaction, + transaction: await this.getTransaction(options), }; const count = await this.count(options); diff --git a/packages/core/database/src/tree-repository/adjacency-list-repository.ts b/packages/core/database/src/tree-repository/adjacency-list-repository.ts index 88d7b3295..5681f35fb 100644 --- a/packages/core/database/src/tree-repository/adjacency-list-repository.ts +++ b/packages/core/database/src/tree-repository/adjacency-list-repository.ts @@ -44,9 +44,7 @@ export class AdjacencyListRepository extends Repository { }); } - const childInstances = (await super.find(findChildrenOptions)).map((r) => { - return r.toJSON(); - }); + const childInstances = await super.find(findChildrenOptions); const nodeMap = {}; @@ -64,10 +62,10 @@ export class AdjacencyListRepository extends Repository { return []; } - return children.map((child) => ({ - ...child, - [childrenKey]: buildTree(child.id), - })); + return children.map((child) => { + child.setDataValue(childrenKey, buildTree(child.id)); + return child; + }); } for (const parent of parentNodes) { diff --git a/packages/plugins/collection-manager/src/__tests__/http-api/find.test.ts b/packages/plugins/collection-manager/src/__tests__/http-api/find.test.ts new file mode 100644 index 000000000..c1db34cf0 --- /dev/null +++ b/packages/plugins/collection-manager/src/__tests__/http-api/find.test.ts @@ -0,0 +1,128 @@ +import { Database } from '@nocobase/database'; +import { MockServer } from '@nocobase/test'; +import { createApp } from '../index'; + +describe('find with association', () => { + let app: MockServer; + let agent; + + let db: Database; + beforeEach(async () => { + app = await createApp(); + + agent = app.agent(); + db = app.db; + }); + + afterEach(async () => { + await app.destroy(); + }); + + it('should filter by association field', async () => { + await db.getRepository('collections').create({ + values: { + name: 'users', + tree: 'adjacency-list', + fields: [ + { type: 'string', name: 'name' }, + { type: 'hasMany', name: 'posts', target: 'posts', foreignKey: 'user_id' }, + { + type: 'belongsTo', + name: 'parent', + foreignKey: 'parent_id', + treeParent: true, + target: 'users', + }, + { + type: 'hasMany', + name: 'children', + foreignKey: 'parent_id', + treeChildren: true, + target: 'users', + }, + ], + }, + context: {}, + }); + + await db.getRepository('collections').create({ + values: { + name: 'posts', + fields: [ + { type: 'string', name: 'title' }, + { type: 'belongsTo', name: 'user', target: 'users', foreignKey: 'user_id' }, + ], + }, + context: {}, + }); + + const UserCollection = db.getCollection('users'); + + expect(UserCollection.options.tree).toBeTruthy(); + + await db.getRepository('users').create({ + values: [ + { + name: 'u1', + posts: [ + { + title: 'u1p1', + }, + ], + children: [ + { + name: 'u2', + posts: [ + { + title: '标题2', + }, + ], + }, + ], + }, + { + name: 'u3', + children: [ + { + name: 'u4', + posts: [ + { + title: '标题五', + }, + ], + }, + ], + }, + ], + }); + + const filter = { + $and: [ + { + children: { + posts: { + title: { + $eq: '标题五', + }, + }, + }, + }, + ], + }; + + const items = await db.getRepository('users').find({ + filter, + appends: ['children'], + }); + + expect(items[0].name).toEqual('u3'); + + const response2 = await agent.resource('users').list({ + filter, + appends: ['children'], + }); + + expect(response2.statusCode).toEqual(200); + expect(response2.body.data[0].name).toEqual('u3'); + }); +});