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:
ChengLei Shao 2022-04-19 16:35:44 +08:00 committed by GitHub
parent 28f9b902e8
commit a37609e71b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 184 additions and 5 deletions

View File

@ -55,7 +55,7 @@ jobs:
run: yarn test -i
env:
DB_DIALECT: sqlite
DB_STORAGE: ":memory:"
DB_STORAGE: /tmp/db.sqlite
- name: Test with MySQL
run: yarn test -i
env:

View File

@ -11,7 +11,6 @@ export type RepositoryType = typeof Repository;
export type CollectionSortable = string | boolean | { name?: string; scopeKey?: string };
export interface CollectionOptions extends Omit<ModelOptions, 'name' | 'hooks'> {
name: string;
tableName?: string;

View File

@ -1,4 +1,5 @@
import _ from 'lodash';
// @ts-ignore
import { pathToRegexp } from 'path-to-regexp';
import qs from 'qs';
import { ResourceType } from './resource';

View File

@ -2,12 +2,18 @@ import { mockServer } from '@nocobase/test';
import PluginUiSchema from '@nocobase/plugin-ui-schema-storage';
import CollectionManagerPlugin from '..';
import lodash from 'lodash';
export async function createApp() {
export async function createApp(options = {}) {
const app = mockServer();
await app.cleanDb();
if (lodash.get(options, 'cleanDB', true)) {
await app.cleanDb();
}
app.plugin(CollectionManagerPlugin);
app.plugin(PluginUiSchema);
await app.load();
return app;
}

View File

@ -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();
});
});

View File

@ -44,6 +44,7 @@ export class CollectionModel extends MagicAttributeModel {
async loadFields(options: Transactionable = {}) {
// @ts-ignore
const instances: FieldModel[] = await this.getFields(options);
for (const instance of instances) {
await instance.load(options);
}

View File

@ -8,7 +8,7 @@ import {
afterCreateForReverseField,
beforeCreateForChildrenCollection,
beforeCreateForReverseField,
beforeInitOptions
beforeInitOptions,
} from './hooks';
import { CollectionModel, FieldModel } from './models';

View File

@ -10,6 +10,30 @@ export class CollectionRepository extends Repository {
async load(options: LoadOptions = {}) {
const { filter, skipExist } = options;
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) {
await instance.load({ skipExist });
}