fix: through table primaryKey error (#297)
* fix: through table primaryKey error * chore: sort fields * chore: test release * chore: github action sqlite env
This commit is contained in:
parent
28f9b902e8
commit
a37609e71b
2
.github/workflows/node-ci.yml
vendored
2
.github/workflows/node-ci.yml
vendored
@ -55,7 +55,7 @@ jobs:
|
|||||||
run: yarn test -i
|
run: yarn test -i
|
||||||
env:
|
env:
|
||||||
DB_DIALECT: sqlite
|
DB_DIALECT: sqlite
|
||||||
DB_STORAGE: ":memory:"
|
DB_STORAGE: /tmp/db.sqlite
|
||||||
- name: Test with MySQL
|
- name: Test with MySQL
|
||||||
run: yarn test -i
|
run: yarn test -i
|
||||||
env:
|
env:
|
||||||
|
@ -11,7 +11,6 @@ export type RepositoryType = typeof Repository;
|
|||||||
|
|
||||||
export type CollectionSortable = string | boolean | { name?: string; scopeKey?: string };
|
export type CollectionSortable = string | boolean | { name?: string; scopeKey?: string };
|
||||||
|
|
||||||
|
|
||||||
export interface CollectionOptions extends Omit<ModelOptions, 'name' | 'hooks'> {
|
export interface CollectionOptions extends Omit<ModelOptions, 'name' | 'hooks'> {
|
||||||
name: string;
|
name: string;
|
||||||
tableName?: string;
|
tableName?: string;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
// @ts-ignore
|
||||||
import { pathToRegexp } from 'path-to-regexp';
|
import { pathToRegexp } from 'path-to-regexp';
|
||||||
import qs from 'qs';
|
import qs from 'qs';
|
||||||
import { ResourceType } from './resource';
|
import { ResourceType } from './resource';
|
||||||
|
@ -2,12 +2,18 @@ import { mockServer } from '@nocobase/test';
|
|||||||
import PluginUiSchema from '@nocobase/plugin-ui-schema-storage';
|
import PluginUiSchema from '@nocobase/plugin-ui-schema-storage';
|
||||||
|
|
||||||
import CollectionManagerPlugin from '..';
|
import CollectionManagerPlugin from '..';
|
||||||
|
import lodash from 'lodash';
|
||||||
|
|
||||||
export async function createApp() {
|
export async function createApp(options = {}) {
|
||||||
const app = mockServer();
|
const app = mockServer();
|
||||||
await app.cleanDb();
|
|
||||||
|
if (lodash.get(options, 'cleanDB', true)) {
|
||||||
|
await app.cleanDb();
|
||||||
|
}
|
||||||
|
|
||||||
app.plugin(CollectionManagerPlugin);
|
app.plugin(CollectionManagerPlugin);
|
||||||
app.plugin(PluginUiSchema);
|
app.plugin(PluginUiSchema);
|
||||||
|
|
||||||
await app.load();
|
await app.load();
|
||||||
return app;
|
return app;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,148 @@
|
|||||||
|
import { mockServer, MockServer } from '@nocobase/test';
|
||||||
|
import CollectionManagerPlugin from '../plugin';
|
||||||
|
|
||||||
|
describe('collections repository', () => {
|
||||||
|
let app: MockServer;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
app = mockServer({
|
||||||
|
database: {
|
||||||
|
tablePrefix: 'through_',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
await app.cleanDb();
|
||||||
|
app.plugin(CollectionManagerPlugin);
|
||||||
|
await app.load();
|
||||||
|
await app.install({ clean: true });
|
||||||
|
await app.start();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
await app.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('case 1', async () => {
|
||||||
|
await app
|
||||||
|
.agent()
|
||||||
|
.resource('collections')
|
||||||
|
.create({
|
||||||
|
values: {
|
||||||
|
name: 'resumes',
|
||||||
|
createdBy: true,
|
||||||
|
updatedBy: true,
|
||||||
|
sortable: true,
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'id',
|
||||||
|
type: 'integer',
|
||||||
|
autoIncrement: true,
|
||||||
|
primaryKey: true,
|
||||||
|
allowNull: false,
|
||||||
|
uiSchema: { type: 'number', title: '{{t("ID")}}', 'x-component': 'InputNumber', 'x-read-pretty': true },
|
||||||
|
interface: 'id',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
title: '简历',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await app
|
||||||
|
.agent()
|
||||||
|
.resource('collections')
|
||||||
|
.create({
|
||||||
|
values: {
|
||||||
|
name: 'jobs',
|
||||||
|
createdBy: true,
|
||||||
|
updatedBy: true,
|
||||||
|
sortable: true,
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'id',
|
||||||
|
type: 'integer',
|
||||||
|
autoIncrement: true,
|
||||||
|
primaryKey: true,
|
||||||
|
allowNull: false,
|
||||||
|
uiSchema: { type: 'number', title: '{{t("ID")}}', 'x-component': 'InputNumber', 'x-read-pretty': true },
|
||||||
|
interface: 'id',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
title: '职位',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await app
|
||||||
|
.agent()
|
||||||
|
.resource('collections')
|
||||||
|
.create({
|
||||||
|
values: {
|
||||||
|
name: 'matches',
|
||||||
|
createdBy: true,
|
||||||
|
updatedBy: true,
|
||||||
|
sortable: true,
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'id',
|
||||||
|
type: 'integer',
|
||||||
|
autoIncrement: true,
|
||||||
|
primaryKey: true,
|
||||||
|
allowNull: false,
|
||||||
|
uiSchema: { type: 'number', title: '{{t("ID")}}', 'x-component': 'InputNumber', 'x-read-pretty': true },
|
||||||
|
interface: 'id',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
title: '匹配',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await app
|
||||||
|
.agent()
|
||||||
|
.resource('collections.fields', 'resumes')
|
||||||
|
.create({
|
||||||
|
values: {
|
||||||
|
name: 'jobs',
|
||||||
|
type: 'belongsToMany',
|
||||||
|
uiSchema: {
|
||||||
|
'x-component': 'RecordPicker',
|
||||||
|
'x-component-props': { multiple: true, fieldNames: { label: 'id', value: 'id' } },
|
||||||
|
title: '职位',
|
||||||
|
},
|
||||||
|
reverseField: {
|
||||||
|
interface: 'linkTo',
|
||||||
|
type: 'belongsToMany',
|
||||||
|
uiSchema: {
|
||||||
|
'x-component': 'RecordPicker',
|
||||||
|
'x-component-props': { multiple: true, fieldNames: { label: 'id', value: 'id' } },
|
||||||
|
title: '简历',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
interface: 'linkTo',
|
||||||
|
target: 'jobs',
|
||||||
|
through: 'matches',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const matchesCollection = app.db.getCollection('matches');
|
||||||
|
|
||||||
|
const matchesFields = [...matchesCollection.fields.entries()];
|
||||||
|
const matchJobField = matchesFields.find((item) => item[1].options.target == 'jobs');
|
||||||
|
|
||||||
|
expect(matchesCollection.model.rawAttributes[matchJobField[1].options.foreignKey].primaryKey).not.toBeTruthy();
|
||||||
|
|
||||||
|
const app2 = mockServer({
|
||||||
|
database: {
|
||||||
|
tablePrefix: 'through_',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
app2.plugin(CollectionManagerPlugin);
|
||||||
|
await app2.load();
|
||||||
|
await app2.start();
|
||||||
|
|
||||||
|
await app2.db.sync();
|
||||||
|
|
||||||
|
expect(
|
||||||
|
app.db.getCollection('matches').model.rawAttributes[matchJobField[1].options.foreignKey].primaryKey,
|
||||||
|
).not.toBeTruthy();
|
||||||
|
|
||||||
|
await app2.destroy();
|
||||||
|
});
|
||||||
|
});
|
@ -44,6 +44,7 @@ export class CollectionModel extends MagicAttributeModel {
|
|||||||
async loadFields(options: Transactionable = {}) {
|
async loadFields(options: Transactionable = {}) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const instances: FieldModel[] = await this.getFields(options);
|
const instances: FieldModel[] = await this.getFields(options);
|
||||||
|
|
||||||
for (const instance of instances) {
|
for (const instance of instances) {
|
||||||
await instance.load(options);
|
await instance.load(options);
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ import {
|
|||||||
afterCreateForReverseField,
|
afterCreateForReverseField,
|
||||||
beforeCreateForChildrenCollection,
|
beforeCreateForChildrenCollection,
|
||||||
beforeCreateForReverseField,
|
beforeCreateForReverseField,
|
||||||
beforeInitOptions
|
beforeInitOptions,
|
||||||
} from './hooks';
|
} from './hooks';
|
||||||
import { CollectionModel, FieldModel } from './models';
|
import { CollectionModel, FieldModel } from './models';
|
||||||
|
|
||||||
|
@ -10,6 +10,30 @@ export class CollectionRepository extends Repository {
|
|||||||
async load(options: LoadOptions = {}) {
|
async load(options: LoadOptions = {}) {
|
||||||
const { filter, skipExist } = options;
|
const { filter, skipExist } = options;
|
||||||
const instances = (await this.find({ filter })) as CollectionModel[];
|
const instances = (await this.find({ filter })) as CollectionModel[];
|
||||||
|
|
||||||
|
const throughModels = [];
|
||||||
|
|
||||||
|
for (const instance of instances) {
|
||||||
|
// @ts-ignore
|
||||||
|
const fields = await instance.getFields();
|
||||||
|
for (const field of fields) {
|
||||||
|
if (field['type'] === 'belongsToMany') {
|
||||||
|
const throughName = field.options.through;
|
||||||
|
if (throughName) {
|
||||||
|
throughModels.push(throughName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
instances.sort((a, b) => {
|
||||||
|
if (throughModels.includes(a.get('name'))) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
});
|
||||||
|
|
||||||
for (const instance of instances) {
|
for (const instance of instances) {
|
||||||
await instance.load({ skipExist });
|
await instance.load({ skipExist });
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user