Browse Source

Merge pull request #4401 from nocodb/fix/validators

fix: validators
pull/4297/merge
աɨռɢӄաօռɢ 2 years ago committed by GitHub
parent
commit
fd35a4f645
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 30
      packages/nc-gui/components/dlg/TableCreate.vue
  2. 112
      packages/nc-gui/utils/validation.ts
  3. 5
      packages/nocodb/src/lib/meta/api/tableApis.ts

30
packages/nc-gui/components/dlg/TableCreate.vue

@ -32,13 +32,21 @@ const { table, createTable, generateUniqueTitle, tables, project } = useTable(as
const useForm = Form.useForm
const validateDuplicateAlias = (v: string) => (tables.value || []).every((t) => t.title !== (v || '')) || 'Duplicate table alias'
const validators = computed(() => {
return {
title: [
validateTableName,
validateDuplicateAlias,
{
validator: (_: any, value: any) => {
// validate duplicate alias
return new Promise((resolve, reject) => {
if ((tables.value || []).some((t) => t.title === (value || ''))) {
return reject(new Error('Duplicate table alias'))
}
return resolve(true)
})
},
},
{
validator: (rule: any, value: any) => {
return new Promise<void>((resolve, reject) => {
@ -62,13 +70,23 @@ const validators = computed(() => {
table_name: [validateTableName],
}
})
const { validateInfos } = useForm(table, validators)
const { validate, validateInfos } = useForm(table, validators)
const systemColumnsCheckboxInfo = SYSTEM_COLUMNS.map((c, index) => ({
value: c,
disabled: index === 0,
}))
const _createTable = async () => {
try {
await validate()
} catch (e: any) {
e.errorFields.map((f: Record<string, any>) => message.error(f.errors.join(',')))
if (e.errorFields.length) return
}
await createTable()
}
onMounted(() => {
generateUniqueTitle()
@ -87,11 +105,11 @@ onMounted(() => {
<template #footer>
<a-button key="back" size="large" @click="dialogShow = false">{{ $t('general.cancel') }}</a-button>
<a-button key="submit" size="large" type="primary" @click="createTable()">{{ $t('general.submit') }}</a-button>
<a-button key="submit" size="large" type="primary" @click="_createTable">{{ $t('general.submit') }}</a-button>
</template>
<div class="pl-10 pr-10 pt-5">
<a-form :model="table" name="create-new-table-form" @keydown.enter="createTable">
<a-form :model="table" name="create-new-table-form" @keydown.enter="_createTable">
<!-- Create A New Table -->
<div class="prose-xl font-bold self-center my-4">{{ $t('activity.createTable') }}</div>

112
packages/nc-gui/utils/validation.ts

@ -3,80 +3,54 @@ import { getI18n } from '~/plugins/a.i18n'
export const isEmail = (v: string) =>
/^(([^<>()[\].,;:\s@"]+(\.[^<>()[\].,;:\s@"]+)*)|(".+"))@(([^<>()[\].,;:\s@"]+\.)+[^<>()[\].,;:\s@"]{2,})$/i.test(v)
export function validateTableName(v: string, isGQL = false) {
const { t } = getI18n().global
export const validateTableName = {
validator: (_: unknown, value: string) => {
return new Promise((resolve, reject) => {
const { t } = getI18n().global
if (!v) {
// return 'Table name required'
return t('msg.error.tableNameRequired')
}
if (!value) {
// return 'Table name required'
return reject(new Error(t('msg.error.tableNameRequired')))
}
// GraphQL naming convention
// http://spec.graphql.org/June2018/#Name
if (isGQL) {
if (/^[_A-Za-z][_0-9A-Za-z]*$/.test(v)) {
return true
}
if (/^[^_A-Za-z]/.test(v)) {
// return 'Name should start with an alphabet or _'
return t('msg.error.nameShouldStartWithAnAlphabetOr_')
}
const m = v.match(/[^_A-Za-z\d]/g)
if (m) {
// return `Following characters are not allowed ${m.map((c) => JSON.stringify(c)).join(', ')}`
return `${t('msg.error.followingCharactersAreNotAllowed')} ${m.map((c) => JSON.stringify(c)).join(', ')}`
}
} else {
// exclude . / \
// rest all characters allowed
// https://documentation.sas.com/doc/en/pgmsascdc/9.4_3.5/acreldb/n0rfg6x1shw0ppn1cwhco6yn09f7.htm#:~:text=By%20default%2C%20MySQL%20encloses%20column,not%20truncate%20a%20longer%20name.
const m = v.match(/[./\\]/g)
if (m) {
// return `Following characters are not allowed ${m.map((c) => JSON.stringify(c)).join(', ')}`
return `${t('msg.error.followingCharactersAreNotAllowed')} ${m.map((c) => JSON.stringify(c)).join(', ')}`
}
return true
}
// exclude . / \
// rest all characters allowed
// https://documentation.sas.com/doc/en/pgmsascdc/9.4_3.5/acreldb/n0rfg6x1shw0ppn1cwhco6yn09f7.htm#:~:text=By%20default%2C%20MySQL%20encloses%20column,not%20truncate%20a%20longer%20name.
const m = value.match(/[./\\]/g)
if (m) {
// return `Following characters are not allowed ${m.map((c) => JSON.stringify(c)).join(', ')}`
return reject(
new Error(`${t('msg.error.followingCharactersAreNotAllowed')} ${m.map((c) => JSON.stringify(c)).join(', ')}`),
)
}
return resolve(true)
})
},
}
export function validateColumnName(v: string, isGQL = false) {
const { t } = getI18n().global
if (!v) {
// return 'Column name required'
return t('msg.error.columnNameRequired')
}
export const validateColumnName = {
validator: (_: unknown, value: string) => {
return new Promise((resolve, reject) => {
const { t } = getI18n().global
// GraphQL naming convention
// http://spec.graphql.org/June2018/#Name
if (isGQL) {
if (/^[_A-Za-z][_0-9A-Za-z]*$/.test(v)) {
return true
}
if (/^[^_A-Za-z]/.test(v)) {
// return 'Name should start with an alphabet or _'
return t('msg.error.nameShouldStartWithAnAlphabetOr_')
}
const m = v.match(/[^_A-Za-z\d]/g)
if (m) {
// return `Following characters are not allowed ${m.map((c) => JSON.stringify(c)).join(', ')}`
return `${t('msg.error.followingCharactersAreNotAllowed')} ${m.map((c) => JSON.stringify(c)).join(', ')}`
}
} else {
// exclude . / \
// rest all characters allowed
// https://documentation.sas.com/doc/en/pgmsascdc/9.4_3.5/acreldb/n0rfg6x1shw0ppn1cwhco6yn09f7.htm#:~:text=By%20default%2C%20MySQL%20encloses%20column,not%20truncate%20a%20longer%20name.
const m = v.match(/[./\\]/g)
if (m) {
// return `Following characters are not allowed ${m.map((c) => JSON.stringify(c)).join(', ')}`
return `${t('msg.error.followingCharactersAreNotAllowed')} ${m.map((c) => JSON.stringify(c)).join(', ')}`
}
return true
}
if (!value) {
// return 'Column name required'
return reject(new Error(t('msg.error.columnNameRequired')))
}
// exclude . / \
// rest all characters allowed
// https://documentation.sas.com/doc/en/pgmsascdc/9.4_3.5/acreldb/n0rfg6x1shw0ppn1cwhco6yn09f7.htm#:~:text=By%20default%2C%20MySQL%20encloses%20column,not%20truncate%20a%20longer%20name.
const m = value.match(/[./\\]/g)
if (m) {
// return `Following characters are not allowed ${m.map((c) => JSON.stringify(c)).join(', ')}`
return reject(
new Error(`${t('msg.error.followingCharactersAreNotAllowed')} ${m.map((c) => JSON.stringify(c)).join(', ')}`),
)
}
return resolve(true)
})
},
}
export const projectTitleValidator = {

5
packages/nocodb/src/lib/meta/api/tableApis.ts

@ -91,7 +91,10 @@ export async function tableCreate(req: Request<any, any, TableReqType>, res) {
const project = await Project.getWithInfo(req.params.projectId);
const base = project.bases[0];
if (!req.body.table_name) {
if (
!req.body.table_name ||
(project.prefix && project.prefix === req.body.table_name)
) {
NcError.badRequest(
'Missing table name `table_name` property in request body'
);

Loading…
Cancel
Save