Skip to content

Commit

Permalink
chore(data-source-main): field name exists error message (nocobase#4689)
Browse files Browse the repository at this point in the history
* chore: test

* chore: throw field name exists error

* chore: error message

* chore: error locale

* fix: i18n message
  • Loading branch information
chareice authored Jun 18, 2024
1 parent f61b56f commit bb82faf
Show file tree
Hide file tree
Showing 8 changed files with 138 additions and 0 deletions.
1 change: 1 addition & 0 deletions packages/core/server/src/locale/locale.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ export class Locale {
// empty
}
}

Object.keys(resources).forEach((name) => {
this.app.i18n.addResources(lang, name, resources[name]);
});
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"field-name-exists": "Field name \"{{name}}\" already exists in collection \"{{collectionName}}\""
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"field-name-exists": "字段标识 \"{{name}}\" 已存在"
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ describe('collections repository', () => {
},
context: {},
});

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

expect(field2.toJSON()).toMatchObject({
type: 'belongsTo',
collectionName: 'foos',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand Down
Original file line number Diff line number Diff line change
@@ -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';
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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) => {
Expand Down Expand Up @@ -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({
Expand Down

0 comments on commit bb82faf

Please sign in to comment.