Prevent invalid characters in column names when importing tables

This commit is contained in:
Andrew Kingston 2023-07-20 12:21:09 +01:00
parent 6a125aadc0
commit 5d0918a6cb
5 changed files with 38 additions and 32 deletions

View File

@ -47,7 +47,7 @@
</svg> </svg>
{#if tooltip && showTooltip} {#if tooltip && showTooltip}
<div class="tooltip" in:fade={{ duration: 130, delay: 250 }}> <div class="tooltip" in:fade={{ duration: 130, delay: 250 }}>
<Tooltip textWrapping direction="bottom" text={tooltip} /> <Tooltip textWrapping direction="top" text={tooltip} />
</div> </div>
{/if} {/if}
</div> </div>
@ -80,15 +80,9 @@
position: absolute; position: absolute;
pointer-events: none; pointer-events: none;
left: 50%; left: 50%;
top: calc(100% + 4px); bottom: calc(100% + 4px);
width: 100vw;
max-width: 150px;
transform: translateX(-50%); transform: translateX(-50%);
text-align: center; text-align: center;
} z-index: 1;
.spectrum-Icon--sizeXS {
width: 10px;
height: 10px;
} }
</style> </style>

View File

@ -33,6 +33,7 @@
import { getBindings } from "components/backend/DataTable/formula" import { getBindings } from "components/backend/DataTable/formula"
import { getContext } from "svelte" import { getContext } from "svelte"
import JSONSchemaModal from "./JSONSchemaModal.svelte" import JSONSchemaModal from "./JSONSchemaModal.svelte"
import { ValidColumnNameRegex } from "@budibase/shared-core"
const AUTO_TYPE = "auto" const AUTO_TYPE = "auto"
const FORMULA_TYPE = FIELDS.FORMULA.type const FORMULA_TYPE = FIELDS.FORMULA.type
@ -379,7 +380,7 @@
const newError = {} const newError = {}
if (!external && fieldInfo.name?.startsWith("_")) { if (!external && fieldInfo.name?.startsWith("_")) {
newError.name = `Column name cannot start with an underscore.` newError.name = `Column name cannot start with an underscore.`
} else if (fieldInfo.name && !fieldInfo.name.match(/^[_a-zA-Z0-9\s]*$/g)) { } else if (fieldInfo.name && !fieldInfo.name.match(ValidColumnNameRegex)) {
newError.name = `Illegal character; must be alpha-numeric.` newError.name = `Illegal character; must be alpha-numeric.`
} else if (PROHIBITED_COLUMN_NAMES.some(name => fieldInfo.name === name)) { } else if (PROHIBITED_COLUMN_NAMES.some(name => fieldInfo.name === name)) {
newError.name = `${PROHIBITED_COLUMN_NAMES.join( newError.name = `${PROHIBITED_COLUMN_NAMES.join(

View File

@ -1,5 +1,5 @@
<script> <script>
import { Select } from "@budibase/bbui" import { Select, Icon } from "@budibase/bbui"
import { FIELDS } from "constants/backend" import { FIELDS } from "constants/backend"
import { API } from "api" import { API } from "api"
import { parseFile } from "./utils" import { parseFile } from "./utils"
@ -12,6 +12,7 @@
let loading = false let loading = false
let validation = {} let validation = {}
let validateHash = "" let validateHash = ""
let errors = {}
export let rows = [] export let rows = []
export let schema = {} export let schema = {}
@ -69,18 +70,20 @@
async function validate(rows, schema) { async function validate(rows, schema) {
loading = true loading = true
error = null
validation = {}
allValid = false
try { try {
if (rows.length > 0) { if (rows.length > 0) {
const response = await API.validateNewTableImport({ rows, schema }) const response = await API.validateNewTableImport({ rows, schema })
validation = response.schemaValidation validation = response.schemaValidation
allValid = response.allValid allValid = response.allValid
errors = response.errors
error = null
} }
} catch (e) { } catch (e) {
error = e.message error = e.message
validation = {}
allValid = false
errors = {}
} }
loading = false loading = false
@ -147,16 +150,22 @@
disabled={loading} disabled={loading}
/> />
<span <span
class={loading || validation[column.name] class={validation[column.name]
? "fieldStatusSuccess" ? "fieldStatusSuccess"
: "fieldStatusFailure"} : "fieldStatusFailure"}
> >
{validation[column.name] ? "Success" : "Failure"} {#if validation[column.name]}
Success
{:else}
Failure
<Icon name="Help" tooltip={errors[column.name]} />
{/if}
</span> </span>
<i <Icon
class={`omit-button ri-close-circle-fill ${ size="S"
loading ? "omit-button-disabled" : "" name="Close"
}`} disabled={loading}
hoverable
on:click={() => { on:click={() => {
delete schema[column.name] delete schema[column.name]
schema = schema schema = schema
@ -237,23 +246,16 @@
justify-self: center; justify-self: center;
font-weight: 600; font-weight: 600;
} }
.fieldStatusFailure { .fieldStatusFailure {
color: var(--red); color: var(--red);
justify-self: center; justify-self: center;
font-weight: 600; font-weight: 600;
display: flex;
align-items: center;
gap: 4px;
} }
.fieldStatusFailure :global(.spectrum-Icon) {
.omit-button { width: 12px;
font-size: 1.2em;
color: var(--grey-7);
cursor: pointer;
justify-self: flex-end;
}
.omit-button-disabled {
pointer-events: none;
opacity: 70%;
} }
.display-column { .display-column {

View File

@ -1,4 +1,5 @@
import { FieldTypes } from "../constants" import { FieldTypes } from "../constants"
import { ValidColumnNameRegex } from "@budibase/shared-core"
interface SchemaColumn { interface SchemaColumn {
readonly name: string readonly name: string
@ -27,6 +28,7 @@ interface ValidationResults {
schemaValidation: SchemaValidation schemaValidation: SchemaValidation
allValid: boolean allValid: boolean
invalidColumns: Array<string> invalidColumns: Array<string>
errors: Record<string, string>
} }
const PARSERS: any = { const PARSERS: any = {
@ -69,6 +71,7 @@ export function validate(rows: Rows, schema: Schema): ValidationResults {
schemaValidation: {}, schemaValidation: {},
allValid: false, allValid: false,
invalidColumns: [], invalidColumns: [],
errors: {},
} }
rows.forEach(row => { rows.forEach(row => {
@ -79,6 +82,11 @@ export function validate(rows: Rows, schema: Schema): ValidationResults {
// If the columnType is not a string, then it's not present in the schema, and should be added to the invalid columns array // If the columnType is not a string, then it's not present in the schema, and should be added to the invalid columns array
if (typeof columnType !== "string") { if (typeof columnType !== "string") {
results.invalidColumns.push(columnName) results.invalidColumns.push(columnName)
} else if (!columnName.match(ValidColumnNameRegex)) {
// Check for special characters in column names
results.invalidColumns.push(columnName)
results.errors[columnName] =
"Column names can't contain special characters"
} else if ( } else if (
columnData == null && columnData == null &&
!schema[columnName].constraints?.presence !schema[columnName].constraints?.presence

View File

@ -89,3 +89,4 @@ export enum BuilderSocketEvent {
} }
export const SocketSessionTTL = 60 export const SocketSessionTTL = 60
export const ValidColumnNameRegex = /^[_a-zA-Z0-9\s]*$/g