chore(data-source-main): field name exists error message (#4689)

* chore: test

* chore: throw field name exists error

* chore: error message

* chore: error locale

* fix: i18n message
This commit is contained in:
ChengLei Shao 2024-06-18 16:34:35 +08:00 committed by GitHub
parent f61b56fe4f
commit bb82faf583
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 138 additions and 0 deletions

View File

@ -109,6 +109,7 @@ export class Locale {
// empty
}
}
Object.keys(resources).forEach((name) => {
this.app.i18n.addResources(lang, name, resources[name]);
});

View File

@ -0,0 +1,3 @@
{
"field-name-exists": "Field name \"{{name}}\" already exists in collection \"{{collectionName}}\""
}

View File

@ -0,0 +1,3 @@
{
"field-name-exists": "字段标识 \"{{name}}\" 已存在"
}

View File

@ -66,6 +66,7 @@ describe('collections repository', () => {
},
context: {},
});
expect(field1.toJSON()).toMatchObject({
type: 'belongsToMany',
collectionName: 'foos',
@ -81,6 +82,7 @@ describe('collections repository', () => {
},
context: {},
});
expect(field2.toJSON()).toMatchObject({
type: 'belongsTo',
collectionName: 'foos',

View File

@ -132,6 +132,35 @@ describe('collections repository', () => {
await app.destroy();
});
it('should throw error when field name already exists', async () => {
await Field.repository.create({
values: {
name: 'name',
type: 'string',
collectionName: 'tests',
},
});
let error;
try {
await Field.repository.create({
values: {
name: 'name',
type: 'string',
collectionName: 'tests',
},
context: {},
});
} catch (err) {
error = err;
}
expect(error).toBeDefined();
expect(error.name).toBe('FieldNameExistsError');
expect(error.value).toBe('name');
expect(error.collectionName).toBe('tests');
});
it('should generate the name and key randomly', async () => {
const field = await Field.repository.create({
values: {

View File

@ -84,6 +84,41 @@ describe('collections repository', () => {
await app.destroy();
});
it('should throw error when create field with same name', async () => {
const response = await agent.resource('collections').create({
values: {
name: 'test',
autoGenId: false,
sortable: false,
timestamps: false,
},
});
expect(response.statusCode).toBe(200);
const response2 = await agent.resource('fields').create({
values: {
name: 'field',
type: 'string',
collectionName: 'test',
},
});
expect(response2.statusCode).toBe(200);
const response3 = await agent.resource('fields').create({
values: {
name: 'field',
type: 'string',
collectionName: 'test',
},
});
expect(response3.statusCode).toBe(400);
const responseBody = response3.body;
console.log(responseBody);
});
it('should skip sync when create empty collection', async () => {
const response = await agent.resource('collections').create({
values: {

View File

@ -0,0 +1,21 @@
/**
* This file is part of the NocoBase (R) project.
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
* Authors: NocoBase Team.
*
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
* For more information, please refer to: https://www.nocobase.com/agreement.
*/
export class FieldNameExistsError extends Error {
value: string;
collectionName: string;
constructor(value: string, collectionName: string) {
super(`Field name "${value}" already exists in collection "${collectionName}"`);
this.value = value;
this.collectionName = collectionName;
this.name = 'FieldNameExistsError';
}
}

View File

@ -27,6 +27,7 @@ import { beforeCreateForViewCollection } from './hooks/beforeCreateForViewCollec
import { CollectionModel, FieldModel } from './models';
import collectionActions from './resourcers/collections';
import viewResourcer from './resourcers/views';
import { FieldNameExistsError } from './errors/field-name-exists-error';
export class PluginDataSourceMainServer extends Plugin {
public schema: string;
@ -128,6 +129,30 @@ export class PluginDataSourceMainServer extends Plugin {
this.app.db.on('fields.beforeCreate', beforeCreateForValidateField(this.app.db));
this.app.db.on('fields.afterCreate', afterCreateForReverseField(this.app.db));
this.app.db.on('fields.beforeCreate', async (model: FieldModel, options) => {
const { transaction } = options;
// validate field name
const collectionName = model.get('collectionName');
const name = model.get('name');
if (!collectionName || !name) {
return;
}
const exists = await this.app.db.getRepository('fields').findOne({
filter: {
collectionName,
name,
},
transaction,
});
if (exists) {
throw new FieldNameExistsError(name, collectionName);
}
});
this.app.db.on('fields.beforeUpdate', beforeUpdateForValidateField(this.app.db));
this.app.db.on('fields.beforeUpdate', async (model, options) => {
@ -308,6 +333,25 @@ export class PluginDataSourceMainServer extends Plugin {
},
);
errorHandlerPlugin.errorHandler.register(
(err) => err instanceof FieldNameExistsError,
(err, ctx) => {
ctx.status = 400;
ctx.body = {
errors: [
{
message: ctx.i18n.t('field-name-exists', {
name: err.value,
collectionName: err.collectionName,
ns: 'data-source-main',
}),
},
],
};
},
);
this.app.resourcer.use(async (ctx, next) => {
if (ctx.action.resourceName === 'collections.fields' && ['create', 'update'].includes(ctx.action.actionName)) {
ctx.action.mergeParams({