mirror of
https://github.com/nocobase/nocobase
synced 2024-11-15 07:25:15 +00:00
fix: load view collection belongs to association with source options (#3912)
* chore: view collection belongs to source field test * chore: test * fix: load field with source attribute * chore: test
This commit is contained in:
parent
e7187e536d
commit
4ac2875d51
@ -1,10 +1,7 @@
|
||||
import { Collection } from '../../collection';
|
||||
import Database from '../../database';
|
||||
import { InheritedCollection } from '../../inherited-collection';
|
||||
import { mockDatabase } from '../index';
|
||||
import pgOnly from './helper';
|
||||
|
||||
pgOnly()('sync inherits', () => {
|
||||
describe.runIf(process.env['DB_DIALECT'] === 'postgres')('sync inherits', () => {
|
||||
let db: Database;
|
||||
|
||||
beforeEach(async () => {
|
||||
|
@ -1,3 +0,0 @@
|
||||
const pgOnly = () => (process.env.DB_DIALECT == 'postgres' ? describe : describe.skip);
|
||||
|
||||
export default pgOnly;
|
@ -2,9 +2,8 @@ import { vi } from 'vitest';
|
||||
import { uid } from '@nocobase/utils';
|
||||
import { Database, mockDatabase } from '../../index';
|
||||
import { ViewCollection } from '../../view-collection';
|
||||
import pgOnly from '../inhertits/helper';
|
||||
|
||||
pgOnly()('', () => {
|
||||
describe.runIf(process.env['DB_DIALECT'] === 'postgres')('pg only view', () => {
|
||||
let db: Database;
|
||||
|
||||
beforeEach(async () => {
|
||||
@ -454,4 +453,90 @@ describe('create view', () => {
|
||||
const viewNameField = ViewCollection.getField('name');
|
||||
expect(viewNameField.options.patterns).toEqual(UserCollection.getField('name').options.patterns);
|
||||
});
|
||||
|
||||
it('should set belongs to field via source', async () => {
|
||||
const User = db.collection({
|
||||
name: 'users',
|
||||
fields: [
|
||||
{
|
||||
type: 'string',
|
||||
name: 'name',
|
||||
},
|
||||
{
|
||||
type: 'hasMany',
|
||||
name: 'posts',
|
||||
target: 'posts',
|
||||
foreignKey: 'userId',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const Post = db.collection({
|
||||
name: 'posts',
|
||||
fields: [
|
||||
{
|
||||
type: 'string',
|
||||
name: 'title',
|
||||
},
|
||||
{
|
||||
type: 'belongsTo',
|
||||
name: 'user',
|
||||
foreignKey: 'userId',
|
||||
target: 'users',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
await db.sync();
|
||||
|
||||
await User.repository.create({
|
||||
values: {
|
||||
name: 'foo',
|
||||
posts: [
|
||||
{
|
||||
title: 'bar',
|
||||
},
|
||||
{
|
||||
title: 'baz',
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
const viewName = 'posts_view';
|
||||
|
||||
const dropViewSQL = `DROP VIEW IF EXISTS ${viewName}`;
|
||||
await db.sequelize.query(dropViewSQL);
|
||||
|
||||
const viewSQL = `
|
||||
CREATE VIEW ${viewName} as SELECT users.* FROM ${Post.quotedTableName()} as users
|
||||
`;
|
||||
|
||||
await db.sequelize.query(viewSQL);
|
||||
|
||||
// create view collection
|
||||
const ViewCollection = db.collection({
|
||||
name: viewName,
|
||||
view: true,
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
type: 'string',
|
||||
source: 'posts.name',
|
||||
},
|
||||
{
|
||||
name: 'user',
|
||||
type: 'belongsTo',
|
||||
source: 'posts.user',
|
||||
},
|
||||
],
|
||||
schema: db.inDialect('postgres') ? 'public' : undefined,
|
||||
});
|
||||
|
||||
const post = await ViewCollection.repository.findOne({
|
||||
appends: ['user'],
|
||||
});
|
||||
|
||||
expect(post['user']['name']).toBe('foo');
|
||||
});
|
||||
});
|
||||
|
@ -325,13 +325,15 @@ export class Collection<
|
||||
this.db.logger.warn(
|
||||
`source collection "${sourceCollectionName}" not found for field "${name}" at collection "${this.name}"`,
|
||||
);
|
||||
return null;
|
||||
} else {
|
||||
const sourceField = sourceCollection.fields.get(sourceFieldName);
|
||||
|
||||
if (!sourceField) {
|
||||
this.db.logger.warn(
|
||||
`source field "${sourceFieldName}" not found for field "${name}" at collection "${this.name}"`,
|
||||
`Source field "${sourceFieldName}" not found for field "${name}" at collection "${this.name}". Source collection: "${sourceCollectionName}"`,
|
||||
);
|
||||
return null;
|
||||
} else {
|
||||
options = { ...lodash.omit(sourceField.options, ['name', 'primaryKey']), ...options };
|
||||
}
|
||||
|
@ -149,6 +149,101 @@ describe('view collection', function () {
|
||||
}
|
||||
});
|
||||
|
||||
it('should load view collection belongs to field', async () => {
|
||||
await collectionRepository.create({
|
||||
values: {
|
||||
name: 'users',
|
||||
fields: [
|
||||
{
|
||||
type: 'string',
|
||||
name: 'name',
|
||||
},
|
||||
{
|
||||
type: 'hasMany',
|
||||
name: 'posts',
|
||||
target: 'posts',
|
||||
foreignKey: 'userId',
|
||||
},
|
||||
],
|
||||
},
|
||||
context: {},
|
||||
});
|
||||
|
||||
await collectionRepository.create({
|
||||
values: {
|
||||
name: 'posts',
|
||||
fields: [
|
||||
{
|
||||
type: 'string',
|
||||
name: 'title',
|
||||
},
|
||||
{
|
||||
type: 'belongsTo',
|
||||
name: 'user',
|
||||
foreignKey: 'userId',
|
||||
target: 'users',
|
||||
},
|
||||
],
|
||||
},
|
||||
context: {},
|
||||
});
|
||||
|
||||
await db.getRepository('users').create({
|
||||
values: [
|
||||
{
|
||||
name: 'u1',
|
||||
posts: [
|
||||
{
|
||||
title: 'p1',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const Post = db.getCollection('posts');
|
||||
|
||||
const viewName = `test_view_${uid(6)}`;
|
||||
await db.sequelize.query(`DROP VIEW IF EXISTS ${viewName}`);
|
||||
|
||||
const viewSQL = `
|
||||
CREATE VIEW ${viewName} as SELECT users.* FROM ${Post.quotedTableName()} as users
|
||||
`;
|
||||
|
||||
await db.sequelize.query(viewSQL);
|
||||
|
||||
await collectionRepository.create({
|
||||
values: {
|
||||
name: viewName,
|
||||
view: true,
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
type: 'string',
|
||||
source: 'posts.title',
|
||||
},
|
||||
{
|
||||
name: 'user',
|
||||
type: 'belongsTo',
|
||||
source: 'posts.user',
|
||||
},
|
||||
],
|
||||
schema: db.inDialect('postgres') ? 'public' : undefined,
|
||||
},
|
||||
context: {},
|
||||
});
|
||||
|
||||
// recall loadFields
|
||||
await app.runCommand('restart');
|
||||
|
||||
db = app.db;
|
||||
|
||||
const viewCollection = db.getCollection(viewName);
|
||||
await viewCollection.repository.find({
|
||||
appends: ['user'],
|
||||
});
|
||||
});
|
||||
|
||||
it('should use view collection as through collection', async () => {
|
||||
const User = await collectionRepository.create({
|
||||
values: {
|
||||
@ -168,8 +263,6 @@ describe('view collection', function () {
|
||||
|
||||
const UserCollection = db.getCollection('users');
|
||||
|
||||
console.log(UserCollection);
|
||||
|
||||
await db.getRepository('users').create({
|
||||
values: [{ name: 'u1' }, { name: 'u2' }],
|
||||
});
|
||||
|
@ -91,11 +91,14 @@ export class CollectionRepository extends Repository {
|
||||
submodule: 'CollectionRepository',
|
||||
method: 'load',
|
||||
});
|
||||
|
||||
this.app.setMaintainingMessage(`load ${instanceName} collection`);
|
||||
|
||||
await nameMap[instanceName].load({ skipField });
|
||||
}
|
||||
|
||||
const fieldWithSourceAttributes = new Map<string, Array<string>>();
|
||||
|
||||
// load view fields
|
||||
for (const viewCollectionName of viewCollections) {
|
||||
this.database.logger.debug(`load collection fields`, {
|
||||
@ -103,8 +106,20 @@ export class CollectionRepository extends Repository {
|
||||
method: 'load',
|
||||
viewCollectionName,
|
||||
});
|
||||
|
||||
const skipField = (() => {
|
||||
const fields = nameMap[viewCollectionName].get('fields');
|
||||
|
||||
return fields.filter((field) => field.options?.source).map((field) => field.get('name'));
|
||||
})();
|
||||
|
||||
this.app.setMaintainingMessage(`load ${viewCollectionName} collection fields`);
|
||||
await nameMap[viewCollectionName].loadFields({});
|
||||
|
||||
if (lodash.isArray(skipField) && skipField.length) {
|
||||
fieldWithSourceAttributes.set(viewCollectionName, skipField);
|
||||
}
|
||||
|
||||
await nameMap[viewCollectionName].loadFields({ skipField });
|
||||
}
|
||||
|
||||
// load lazy collection field
|
||||
@ -117,6 +132,18 @@ export class CollectionRepository extends Repository {
|
||||
this.app.setMaintainingMessage(`load ${collectionName} collection fields`);
|
||||
await nameMap[collectionName].loadFields({ includeFields: skipField });
|
||||
}
|
||||
|
||||
// load source attribute fields
|
||||
for (const [collectionName, skipField] of fieldWithSourceAttributes) {
|
||||
this.database.logger.debug(`load collection fields`, {
|
||||
submodule: 'CollectionRepository',
|
||||
method: 'load',
|
||||
collectionName,
|
||||
});
|
||||
|
||||
this.app.setMaintainingMessage(`load ${collectionName} collection fields`);
|
||||
await nameMap[collectionName].loadFields({ includeFields: skipField });
|
||||
}
|
||||
}
|
||||
|
||||
async db2cm(collectionName: string) {
|
||||
|
Loading…
Reference in New Issue
Block a user