mirror of
https://github.com/nocobase/nocobase
synced 2024-11-15 03:56:16 +00:00
chore(action-import): validate association & select field value (#4643)
* chore: validate association value in import * chore: validate value in select field * fix: test
This commit is contained in:
parent
8260cfc817
commit
e56e52ad49
@ -16,12 +16,22 @@ export class MultipleSelectInterface extends BaseInterface {
|
||||
const enumConfig = this.options.uiSchema?.enum || [];
|
||||
return items.map((item) => {
|
||||
const option = enumConfig.find((option) => option.label === item);
|
||||
return option ? option.value : item;
|
||||
if (option) {
|
||||
return option.value;
|
||||
}
|
||||
|
||||
const valueOption = enumConfig.find((option) => option.value === item);
|
||||
if (valueOption) {
|
||||
return valueOption.value;
|
||||
}
|
||||
|
||||
throw new Error(`"${item}" is not a valid option in ${ctx.field.name} field.`);
|
||||
});
|
||||
}
|
||||
|
||||
toString(value: any, ctx?: any) {
|
||||
const enumConfig = this.options.uiSchema?.enum || [];
|
||||
|
||||
return lodash
|
||||
.castArray(value)
|
||||
.map((value) => {
|
||||
|
@ -11,9 +11,22 @@ import { BaseInterface } from './base-interface';
|
||||
|
||||
export class SelectInterface extends BaseInterface {
|
||||
async toValue(str: string, ctx?: any): Promise<any> {
|
||||
if (!str) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const enumConfig = this.options.uiSchema?.enum || [];
|
||||
const option = enumConfig.find((item) => item.label === str);
|
||||
return option?.value || str;
|
||||
if (option) {
|
||||
return option.value;
|
||||
}
|
||||
|
||||
const valueOption = enumConfig.find((item) => item.value === str);
|
||||
if (valueOption) {
|
||||
return valueOption.value;
|
||||
}
|
||||
|
||||
throw new Error(`"${str}" is not a valid option in ${ctx.field.name} field.`);
|
||||
}
|
||||
|
||||
toString(value: any, ctx?: any) {
|
||||
|
@ -26,6 +26,13 @@ export class ToManyInterface extends BaseInterface {
|
||||
transaction,
|
||||
});
|
||||
|
||||
// check if all items are found
|
||||
items.forEach((item) => {
|
||||
if (!targetInstances.find((targetInstance) => targetInstance[filterKey] === item)) {
|
||||
throw new Error(`"${item}" not found in ${targetCollection.model.name} ${filterKey}`);
|
||||
}
|
||||
});
|
||||
|
||||
const primaryKeyAttribute = targetCollection.model.primaryKeyAttribute;
|
||||
|
||||
return targetInstances.map((targetInstance) => targetInstance[primaryKeyAttribute]);
|
||||
|
@ -15,6 +15,10 @@ export class ToOneInterface extends BaseInterface {
|
||||
}
|
||||
|
||||
async toValue(str: string, ctx?: any) {
|
||||
if (!str) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { filterKey, targetCollection, transaction } = ctx;
|
||||
|
||||
const targetInstance = await targetCollection.repository.findOne({
|
||||
@ -24,12 +28,11 @@ export class ToOneInterface extends BaseInterface {
|
||||
transaction,
|
||||
});
|
||||
|
||||
if (!targetInstance) {
|
||||
throw new Error(`"${str}" not found in ${targetCollection.model.name} ${filterKey}`);
|
||||
}
|
||||
const primaryKeyAttribute = targetCollection.model.primaryKeyAttribute;
|
||||
|
||||
if (targetInstance) {
|
||||
return targetInstance[primaryKeyAttribute];
|
||||
}
|
||||
|
||||
return null;
|
||||
return targetInstance[primaryKeyAttribute];
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,326 @@ describe('xlsx importer', () => {
|
||||
await app.destroy();
|
||||
});
|
||||
|
||||
describe('import with select fields', () => {
|
||||
let User;
|
||||
beforeEach(async () => {
|
||||
User = app.db.collection({
|
||||
name: 'users',
|
||||
fields: [
|
||||
{ type: 'string', name: 'name', title: '姓名' },
|
||||
{
|
||||
uiSchema: {
|
||||
enum: [
|
||||
{
|
||||
value: '123',
|
||||
label: 'Label123',
|
||||
color: 'orange',
|
||||
},
|
||||
{
|
||||
value: '223',
|
||||
label: 'Label223',
|
||||
color: 'lime',
|
||||
},
|
||||
],
|
||||
type: 'array',
|
||||
'x-component': 'Select',
|
||||
'x-component-props': {
|
||||
mode: 'multiple',
|
||||
},
|
||||
title: 'multi-select',
|
||||
},
|
||||
defaultValue: [],
|
||||
name: 'multiSelect',
|
||||
type: 'array',
|
||||
interface: 'multipleSelect',
|
||||
},
|
||||
{
|
||||
uiSchema: {
|
||||
enum: [
|
||||
{
|
||||
value: '123',
|
||||
label: 'Label123',
|
||||
color: 'orange',
|
||||
},
|
||||
{
|
||||
value: '223',
|
||||
label: 'Label223',
|
||||
color: 'lime',
|
||||
},
|
||||
],
|
||||
type: 'string',
|
||||
'x-component': 'Select',
|
||||
title: 'select',
|
||||
},
|
||||
name: 'select',
|
||||
type: 'string',
|
||||
interface: 'select',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
await app.db.sync();
|
||||
});
|
||||
|
||||
it('should import select field with label and value', async () => {
|
||||
const columns = [
|
||||
{
|
||||
dataIndex: ['name'],
|
||||
defaultTitle: '姓名',
|
||||
},
|
||||
{
|
||||
dataIndex: ['multiSelect'],
|
||||
defaultTitle: '多选',
|
||||
},
|
||||
{
|
||||
dataIndex: ['select'],
|
||||
defaultTitle: '单选',
|
||||
},
|
||||
];
|
||||
|
||||
const templateCreator = new TemplateCreator({
|
||||
collection: User,
|
||||
columns,
|
||||
});
|
||||
|
||||
const template = await templateCreator.run();
|
||||
|
||||
const worksheet = template.Sheets[template.SheetNames[0]];
|
||||
|
||||
XLSX.utils.sheet_add_aoa(worksheet, [['test', 'Label123,223', 'Label123']], {
|
||||
origin: 'A2',
|
||||
});
|
||||
|
||||
const importer = new XlsxImporter({
|
||||
collectionManager: app.mainDataSource.collectionManager,
|
||||
collection: User,
|
||||
columns,
|
||||
workbook: template,
|
||||
});
|
||||
|
||||
await importer.run();
|
||||
|
||||
expect(await User.repository.count()).toBe(1);
|
||||
|
||||
const user = await User.repository.findOne();
|
||||
|
||||
expect(user.get('multiSelect')).toEqual(['123', '223']);
|
||||
expect(user.get('select')).toBe('123');
|
||||
});
|
||||
|
||||
it('should validate values in multiple select field', async () => {
|
||||
const columns = [
|
||||
{
|
||||
dataIndex: ['name'],
|
||||
defaultTitle: '姓名',
|
||||
},
|
||||
{
|
||||
dataIndex: ['multiSelect'],
|
||||
defaultTitle: '多选',
|
||||
},
|
||||
];
|
||||
|
||||
const templateCreator = new TemplateCreator({
|
||||
collection: User,
|
||||
columns,
|
||||
});
|
||||
|
||||
const template = await templateCreator.run();
|
||||
|
||||
const worksheet = template.Sheets[template.SheetNames[0]];
|
||||
|
||||
XLSX.utils.sheet_add_aoa(worksheet, [['test', 'abc']], {
|
||||
origin: 'A2',
|
||||
});
|
||||
|
||||
const importer = new XlsxImporter({
|
||||
collectionManager: app.mainDataSource.collectionManager,
|
||||
collection: User,
|
||||
columns,
|
||||
workbook: template,
|
||||
});
|
||||
|
||||
let error;
|
||||
|
||||
try {
|
||||
await importer.run();
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
|
||||
expect(error).toBeTruthy();
|
||||
});
|
||||
it('should validate values in select field', async () => {
|
||||
const columns = [
|
||||
{
|
||||
dataIndex: ['name'],
|
||||
defaultTitle: '姓名',
|
||||
},
|
||||
{
|
||||
dataIndex: ['select'],
|
||||
defaultTitle: '单选',
|
||||
},
|
||||
];
|
||||
|
||||
const templateCreator = new TemplateCreator({
|
||||
collection: User,
|
||||
columns,
|
||||
});
|
||||
|
||||
const template = await templateCreator.run();
|
||||
|
||||
const worksheet = template.Sheets[template.SheetNames[0]];
|
||||
|
||||
XLSX.utils.sheet_add_aoa(worksheet, [['test', 'abc']], {
|
||||
origin: 'A2',
|
||||
});
|
||||
|
||||
const importer = new XlsxImporter({
|
||||
collectionManager: app.mainDataSource.collectionManager,
|
||||
collection: User,
|
||||
columns,
|
||||
workbook: template,
|
||||
});
|
||||
|
||||
let error;
|
||||
|
||||
try {
|
||||
await importer.run();
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
|
||||
expect(error).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('import with associations', () => {
|
||||
let User;
|
||||
let Post;
|
||||
beforeEach(async () => {
|
||||
User = app.db.collection({
|
||||
name: 'users',
|
||||
fields: [
|
||||
{
|
||||
type: 'string',
|
||||
name: 'name',
|
||||
},
|
||||
{
|
||||
type: 'hasMany',
|
||||
name: 'posts',
|
||||
target: 'posts',
|
||||
interface: 'o2m',
|
||||
foreignKey: 'userId',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
Post = app.db.collection({
|
||||
name: 'posts',
|
||||
fields: [
|
||||
{
|
||||
type: 'string',
|
||||
name: 'title',
|
||||
},
|
||||
{
|
||||
type: 'belongsTo',
|
||||
name: 'user',
|
||||
target: 'users',
|
||||
interface: 'm2o',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
await app.db.sync();
|
||||
});
|
||||
|
||||
it('should validate to many association', async () => {
|
||||
const columns = [
|
||||
{
|
||||
dataIndex: ['name'],
|
||||
defaultTitle: '名称',
|
||||
},
|
||||
{
|
||||
dataIndex: ['posts', 'title'],
|
||||
defaultTitle: '标题',
|
||||
},
|
||||
];
|
||||
|
||||
const templateCreator = new TemplateCreator({
|
||||
collection: User,
|
||||
columns,
|
||||
});
|
||||
|
||||
const template = await templateCreator.run();
|
||||
|
||||
const worksheet = template.Sheets[template.SheetNames[0]];
|
||||
|
||||
XLSX.utils.sheet_add_aoa(worksheet, [['test', '测试标题']], {
|
||||
origin: 'A2',
|
||||
});
|
||||
|
||||
const importer = new XlsxImporter({
|
||||
collectionManager: app.mainDataSource.collectionManager,
|
||||
collection: User,
|
||||
columns,
|
||||
workbook: template,
|
||||
});
|
||||
|
||||
let error;
|
||||
|
||||
try {
|
||||
await importer.run();
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
|
||||
expect(error).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should validate to one association', async () => {
|
||||
const columns = [
|
||||
{
|
||||
dataIndex: ['title'],
|
||||
defaultTitle: '标题',
|
||||
},
|
||||
{
|
||||
dataIndex: ['user', 'name'],
|
||||
defaultTitle: '用户名',
|
||||
},
|
||||
];
|
||||
|
||||
const templateCreator = new TemplateCreator({
|
||||
collection: Post,
|
||||
columns,
|
||||
});
|
||||
|
||||
const template = await templateCreator.run();
|
||||
|
||||
const worksheet = template.Sheets[template.SheetNames[0]];
|
||||
|
||||
XLSX.utils.sheet_add_aoa(worksheet, [['test title', 'test user']], {
|
||||
origin: 'A2',
|
||||
});
|
||||
|
||||
const importer = new XlsxImporter({
|
||||
collectionManager: app.mainDataSource.collectionManager,
|
||||
collection: Post,
|
||||
columns,
|
||||
workbook: template,
|
||||
});
|
||||
|
||||
let error;
|
||||
|
||||
try {
|
||||
await importer.run();
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
|
||||
expect(error).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
it('should import china region field', async () => {
|
||||
const Post = app.db.collection({
|
||||
name: 'posts',
|
||||
@ -652,7 +972,7 @@ describe('xlsx importer', () => {
|
||||
[
|
||||
['Post1', 'Content1', 'User1', 'Tag1,Tag2', 'Comment1,Comment2'],
|
||||
['Post2', 'Content2', 'User1', 'Tag2,Tag3', 'Comment3'],
|
||||
['Post3', 'Content3', 'UserNotExist', 'Tag3,TagNotExist', ''],
|
||||
['Post3', 'Content3', 'User1', 'Tag3', ''],
|
||||
['Post4', '', '', ''],
|
||||
['Post5', null, null, null],
|
||||
],
|
||||
|
@ -188,7 +188,7 @@ export class XlsxImporter extends EventEmitter {
|
||||
await new Promise((resolve) => setTimeout(resolve, 5));
|
||||
} catch (error) {
|
||||
throw new Error(
|
||||
`failed to import row ${handingRowIndex}, rowData: ${JSON.stringify(rowValues)} message: ${error.message}`,
|
||||
`failed to import row ${handingRowIndex}, message: ${error.message}, rowData: ${JSON.stringify(rowValues)}`,
|
||||
{ cause: error },
|
||||
);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user