Merge pull request #10462 from Budibase/fix/budi-6789-import-data-empty-date-is-not-allowed
Fix - Budi 6789 import data empty date is not allowed
This commit is contained in:
commit
fa6e6b53f8
|
@ -37,7 +37,7 @@ import {
|
||||||
Table,
|
Table,
|
||||||
} from "@budibase/types"
|
} from "@budibase/types"
|
||||||
|
|
||||||
const { cleanExportRows } = require("./utils")
|
import { cleanExportRows } from "./utils"
|
||||||
|
|
||||||
const CALCULATION_TYPES = {
|
const CALCULATION_TYPES = {
|
||||||
SUM: "sum",
|
SUM: "sum",
|
||||||
|
@ -391,6 +391,9 @@ export async function exportRows(ctx: UserCtx) {
|
||||||
const table = await db.get(ctx.params.tableId)
|
const table = await db.get(ctx.params.tableId)
|
||||||
const rowIds = ctx.request.body.rows
|
const rowIds = ctx.request.body.rows
|
||||||
let format = ctx.query.format
|
let format = ctx.query.format
|
||||||
|
if (typeof format !== "string") {
|
||||||
|
ctx.throw(400, "Format parameter is not valid")
|
||||||
|
}
|
||||||
const { columns, query } = ctx.request.body
|
const { columns, query } = ctx.request.body
|
||||||
|
|
||||||
let result
|
let result
|
||||||
|
|
|
@ -137,8 +137,8 @@ export function cleanExportRows(
|
||||||
delete schema[column]
|
delete schema[column]
|
||||||
})
|
})
|
||||||
|
|
||||||
// Intended to avoid 'undefined' in export
|
|
||||||
if (format === Format.CSV) {
|
if (format === Format.CSV) {
|
||||||
|
// Intended to append empty values in export
|
||||||
const schemaKeys = Object.keys(schema)
|
const schemaKeys = Object.keys(schema)
|
||||||
for (let key of schemaKeys) {
|
for (let key of schemaKeys) {
|
||||||
if (columns?.length && columns.indexOf(key) > 0) {
|
if (columns?.length && columns.indexOf(key) > 0) {
|
||||||
|
@ -146,7 +146,7 @@ export function cleanExportRows(
|
||||||
}
|
}
|
||||||
for (let row of cleanRows) {
|
for (let row of cleanRows) {
|
||||||
if (row[key] == null) {
|
if (row[key] == null) {
|
||||||
row[key] = ""
|
row[key] = undefined
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ import { getDatasourceParams } from "../../../db/utils"
|
||||||
import { context, events } from "@budibase/backend-core"
|
import { context, events } from "@budibase/backend-core"
|
||||||
import { Table, UserCtx } from "@budibase/types"
|
import { Table, UserCtx } from "@budibase/types"
|
||||||
import sdk from "../../../sdk"
|
import sdk from "../../../sdk"
|
||||||
import csv from "csvtojson"
|
import { jsonFromCsvString } from "../../../utilities/csv"
|
||||||
|
|
||||||
function pickApi({ tableId, table }: { tableId?: string; table?: Table }) {
|
function pickApi({ tableId, table }: { tableId?: string; table?: Table }) {
|
||||||
if (table && !tableId) {
|
if (table && !tableId) {
|
||||||
|
@ -104,7 +104,7 @@ export async function bulkImport(ctx: UserCtx) {
|
||||||
export async function csvToJson(ctx: UserCtx) {
|
export async function csvToJson(ctx: UserCtx) {
|
||||||
const { csvString } = ctx.request.body
|
const { csvString } = ctx.request.body
|
||||||
|
|
||||||
const result = await csv().fromString(csvString)
|
const result = await jsonFromCsvString(csvString)
|
||||||
|
|
||||||
ctx.status = 200
|
ctx.status = 200
|
||||||
ctx.body = result
|
ctx.body = result
|
||||||
|
|
|
@ -10,7 +10,9 @@ export function csv(headers: string[], rows: Row[]) {
|
||||||
val =
|
val =
|
||||||
typeof val === "object" && !(val instanceof Date)
|
typeof val === "object" && !(val instanceof Date)
|
||||||
? `"${JSON.stringify(val).replace(/"/g, "'")}"`
|
? `"${JSON.stringify(val).replace(/"/g, "'")}"`
|
||||||
: `"${val}"`
|
: val !== undefined
|
||||||
|
? `"${val}"`
|
||||||
|
: ""
|
||||||
return val.trim()
|
return val.trim()
|
||||||
})
|
})
|
||||||
.join(",")}`
|
.join(",")}`
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
import csv from "csvtojson"
|
||||||
|
|
||||||
|
export async function jsonFromCsvString(csvString: string) {
|
||||||
|
const castedWithEmptyValues = await csv({ ignoreEmpty: true }).fromString(
|
||||||
|
csvString
|
||||||
|
)
|
||||||
|
|
||||||
|
// By default the csvtojson library casts empty values as empty strings. This is causing issues on conversion.
|
||||||
|
// ignoreEmpty will remove the key completly if empty, so creating this empty object will ensure we return the values with the keys but empty values
|
||||||
|
const result = await csv({ ignoreEmpty: false }).fromString(csvString)
|
||||||
|
result.forEach((r, i) => {
|
||||||
|
for (const [key] of Object.entries(r).filter(
|
||||||
|
([key, value]) => value === ""
|
||||||
|
)) {
|
||||||
|
if (castedWithEmptyValues[i][key] === undefined) {
|
||||||
|
r[key] = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
|
@ -4,6 +4,9 @@ interface SchemaColumn {
|
||||||
readonly name: string
|
readonly name: string
|
||||||
readonly type: FieldTypes
|
readonly type: FieldTypes
|
||||||
readonly autocolumn?: boolean
|
readonly autocolumn?: boolean
|
||||||
|
readonly constraints?: {
|
||||||
|
presence: boolean
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Schema {
|
interface Schema {
|
||||||
|
@ -76,6 +79,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 (
|
||||||
|
columnData == null &&
|
||||||
|
!schema[columnName].constraints?.presence
|
||||||
|
) {
|
||||||
|
results.schemaValidation[columnName] = true
|
||||||
} else if (
|
} else if (
|
||||||
// If there's no data for this field don't bother with further checks
|
// If there's no data for this field don't bother with further checks
|
||||||
// If the field is already marked as invalid there's no need for further checks
|
// If the field is already marked as invalid there's no need for further checks
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
import { jsonFromCsvString } from "../csv"
|
||||||
|
|
||||||
|
describe("csv", () => {
|
||||||
|
describe("jsonFromCsvString", () => {
|
||||||
|
test("multiple lines csv can be casted", async () => {
|
||||||
|
const csvString = '"id","title"\n"1","aaa"\n"2","bbb"'
|
||||||
|
|
||||||
|
const result = await jsonFromCsvString(csvString)
|
||||||
|
|
||||||
|
expect(result).toEqual([
|
||||||
|
{ id: "1", title: "aaa" },
|
||||||
|
{ id: "2", title: "bbb" },
|
||||||
|
])
|
||||||
|
result.forEach(r => expect(Object.keys(r)).toEqual(["id", "title"]))
|
||||||
|
})
|
||||||
|
|
||||||
|
test("empty values are casted as undefined", async () => {
|
||||||
|
const csvString =
|
||||||
|
'"id","optional","title"\n1,,"aaa"\n2,"value","bbb"\n3,,"ccc"'
|
||||||
|
|
||||||
|
const result = await jsonFromCsvString(csvString)
|
||||||
|
|
||||||
|
expect(result).toEqual([
|
||||||
|
{ id: "1", optional: null, title: "aaa" },
|
||||||
|
{ id: "2", optional: "value", title: "bbb" },
|
||||||
|
{ id: "3", optional: null, title: "ccc" },
|
||||||
|
])
|
||||||
|
result.forEach(r =>
|
||||||
|
expect(Object.keys(r)).toEqual(["id", "optional", "title"])
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
Loading…
Reference in New Issue