1
0
mirror of https://gitlab.com/bramw/baserow.git synced 2024-11-21 23:37:55 +00:00
bramw_baserow/web-frontend/modules/database/mixins/importer.js

193 lines
6.9 KiB
JavaScript

/**
* Mixin that introduces helper methods for the importer form component.
*/
import {
RESERVED_BASEROW_FIELD_NAMES,
MAX_FIELD_NAME_LENGTH,
} from '@baserow/modules/database/utils/constants'
const IMPORT_PREVIEW_MAX_ROW_COUNT = 6
export default {
data() {
return {
fileLoadingProgress: 0,
state: null,
error: '',
values: {},
}
},
computed: {
stateTitle() {
return this.$t(`importer.${this.state}`)
},
},
methods: {
resetImporterState() {
this.state = null
this.error = ''
this.values = {}
this.$emit('getData', null)
this.$emit('data', { header: [], previewData: [] })
},
handleImporterError(error) {
this.resetImporterState()
this.fileLoadingProgress = 0
this.error = error
},
/**
* Adds a header of Field 1, Field 2, etc. if the header is empty,
* otherwise checks that the existing header has valid and non duplicate field
* names. If there are invalid or duplicate field names they will be replaced with
* valid unique field names instead.
*
* @param header An array starting of the column name.
* @param data An array starting with a header row if firstRowHeader is true,
* followed by rows of data.
* @return {*} An updated data object with the first row being a valid unique
* header row.
*/
prepareHeader(header = [], data) {
const columnCount = Math.max.apply(
null,
data.map((entry) => entry.length)
)
// If the first row is not the header, a header containing columns named
// 'Field N' needs to be generated.
if (!header || header.length === 0) {
const newHead = []
for (let i = 1; i <= columnCount; i++) {
newHead.push(this.$t(`importer.fieldDefaultName`, { count: i }))
}
return newHead
} else {
// The header row might not be long enough to cover all columns, ensure it does
// first.
const newHead = header.map((value) => `${value}`)
for (let i = newHead.length; i < columnCount; i++) {
newHead.push(this.$t(`importer.fieldDefaultName`, { count: i }))
}
return this.makeHeaderUniqueAndValid(newHead)
}
},
/**
* Fills the row with a minimum amount of empty columns.
*/
fill(row, maxLength) {
for (let i = row.length; i < maxLength; i++) {
row.push('')
}
return row
},
/**
* Generates an object that can used to render a quick preview of the provided
* data.
*/
getPreview(head, data) {
const rows = data.slice(0, IMPORT_PREVIEW_MAX_ROW_COUNT)
const columns = Math.max.apply(
null,
data.map((entry) => entry.length)
)
rows.map((row) => this.fill(row, columns))
return rows
},
/**
* Find the next un-unused column not present or used yet in the nextFreeIndexMap.
* Will append a number to the returned columnName if it is taken, where that
* number ensures the returned name is unique. Will respect the maximum allowed
* field name length. Finally this function will update
* the nextFreeIndexMap so future calls will not use any columns returned by
* this function.
* @param originalColumnName The column name to find the next free unique value for.
* @param nextFreeIndexMap A map of column name to next free starting index.
* @param startingIndex The starting index to start from if no index is found in
* the map.
* @return {string} A column name possibly postfixed with a number to ensure it
* is unique.
*/
findNextFreeName(originalColumnName, nextFreeIndexMap, startingIndex) {
let i = nextFreeIndexMap.get(originalColumnName) || startingIndex
while (true) {
const suffixToAppend = ` ${i}`
let nextColumnNameToCheck
// If appending a number to the columnName in order to make it
// unique will return a string that is longer than the maximum
// allowed field name length, we need to further slice the
// columnName as to not go above the maximum allowed length.
if (
originalColumnName.length + suffixToAppend.length >
MAX_FIELD_NAME_LENGTH
) {
nextColumnNameToCheck = `${originalColumnName.slice(
0,
-suffixToAppend.length
)}${suffixToAppend}`
} else {
nextColumnNameToCheck = `${originalColumnName}${suffixToAppend}`
}
if (!nextFreeIndexMap.has(nextColumnNameToCheck)) {
nextFreeIndexMap.set(originalColumnName, i + 1)
return nextColumnNameToCheck
}
i++
}
},
/**
* Given a column name this function will return a new name which is guaranteed
* to be unique and valid. If the originally provided name is unique and valid
* then it will be returned untouched.
*
* @param column The column name to check.
* @param nextFreeIndexMap A map of column names to an index number. A value of 0
* indicates that the key is a column name which exists in the table but has not
* yet been returned yet. A number higher than 0 indicates that the column has
* already occurred and the index needs to be appended to the name to generate a
* new unique column name.
* @return {string|*} A valid unique column name.
*/
makeColumnNameUniqueAndValidIfNotAlready(column, nextFreeIndexMap) {
if (column === '') {
return this.findNextFreeName('Field', nextFreeIndexMap, 1)
} else if (RESERVED_BASEROW_FIELD_NAMES.includes(column)) {
return this.findNextFreeName(column, nextFreeIndexMap, 2)
} else if (nextFreeIndexMap.get(column) > 0) {
return this.findNextFreeName(column, nextFreeIndexMap, 2)
} else {
nextFreeIndexMap.set(column, 2)
return column
}
},
/**
* Ensures that the uploaded field names are unique, non blank, don't exceed
* the maximum field name length and don't use any reserved Baserow field names.
* @param {*[]} head An array of field names to be checked.
* @return A new array of field names which are guaranteed to be unique and valid.
*/
makeHeaderUniqueAndValid(head) {
const nextFreeIndexMap = new Map()
for (let i = 0; i < head.length; i++) {
const truncatedColumn = head[i].trim().slice(0, MAX_FIELD_NAME_LENGTH)
nextFreeIndexMap.set(truncatedColumn, 0)
}
const uniqueAndValidHeader = []
for (let i = 0; i < head.length; i++) {
const column = head[i]
const trimmedColumn = column.trim()
const truncatedColumn = trimmedColumn.slice(0, MAX_FIELD_NAME_LENGTH)
const uniqueValidName = this.makeColumnNameUniqueAndValidIfNotAlready(
truncatedColumn,
nextFreeIndexMap
)
uniqueAndValidHeader.push(uniqueValidName)
}
return uniqueAndValidHeader
},
},
}