mirror of https://github.com/nocodb/nocodb
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
168 lines
4.9 KiB
168 lines
4.9 KiB
import { UITypes } from 'nocodb-sdk' |
|
import { |
|
extractMultiOrSingleSelectProps, |
|
getCheckboxValue, |
|
isCheckboxType, |
|
isDecimalType, |
|
isEmailType, |
|
isMultiLineTextType, |
|
isUrlType, |
|
} from './parserHelpers' |
|
import TemplateGenerator from './TemplateGenerator' |
|
|
|
const jsonTypeToUidt: Record<string, string> = { |
|
number: UITypes.Number, |
|
string: UITypes.SingleLineText, |
|
date: UITypes.DateTime, |
|
boolean: UITypes.Checkbox, |
|
object: UITypes.JSON, |
|
} |
|
|
|
const extractNestedData: any = (obj: any, path: any) => path.reduce((val: any, key: any) => val && val[key], obj) |
|
|
|
export default class JSONTemplateAdapter extends TemplateGenerator { |
|
config: Record<string, any> |
|
name: string |
|
data: Record<string, any> |
|
_jsonData: string | Record<string, any> |
|
jsonData: Record<string, any> |
|
project: Record<string, any> |
|
columns: object |
|
constructor(name = 'test', data: object, parserConfig = {}) { |
|
super() |
|
this.config = { |
|
maxRowsToParse: 500, |
|
...parserConfig, |
|
} |
|
this.name = name |
|
this._jsonData = data |
|
this.project = { |
|
title: this.name, |
|
tables: [], |
|
} |
|
this.jsonData = [] |
|
this.data = [] |
|
this.columns = {} |
|
} |
|
|
|
async init() { |
|
const parsedJsonData = |
|
typeof this._jsonData === 'string' |
|
? // for json editor |
|
JSON.parse(this._jsonData) |
|
: // for file upload |
|
JSON.parse(new TextDecoder().decode(this._jsonData as BufferSource)) |
|
this.jsonData = Array.isArray(parsedJsonData) ? parsedJsonData : [parsedJsonData] |
|
} |
|
|
|
getColumns(): any { |
|
return this.columns |
|
} |
|
|
|
getData(): any { |
|
return this.data |
|
} |
|
|
|
parse(): any { |
|
const jsonData = this.jsonData |
|
const tn = 'table' |
|
const table: any = { table_name: tn, ref_table_name: tn, columns: [] } |
|
|
|
this.data[tn] = [] |
|
|
|
for (const col of Object.keys(jsonData[0])) { |
|
const columns = this._parseColumn([col], jsonData) |
|
table.columns.push(...columns) |
|
} |
|
|
|
if (this.config.importData) { |
|
this._parseTableData(table) |
|
} |
|
|
|
this.project.tables.push(table) |
|
} |
|
|
|
getTemplate() { |
|
return this.project |
|
} |
|
|
|
_parseColumn( |
|
path: any = [], |
|
jsonData = this.jsonData, |
|
firstRowVal = path.reduce((val: any, k: any) => val && val[k], this.jsonData[0]), |
|
): any { |
|
const columns = [] |
|
// parse nested |
|
if (firstRowVal && typeof firstRowVal === 'object' && !Array.isArray(firstRowVal) && this.config.normalizeNested) { |
|
for (const key of Object.keys(firstRowVal)) { |
|
const normalizedNestedColumns = this._parseColumn([...path, key], this.jsonData, firstRowVal[key]) |
|
columns.push(...normalizedNestedColumns) |
|
} |
|
} else { |
|
const cn = path.join('_').replace(/\W/g, '_').trim() |
|
|
|
const column: Record<string, any> = { |
|
column_name: cn, |
|
ref_column_name: cn, |
|
path, |
|
} |
|
|
|
column.uidt = jsonTypeToUidt[typeof firstRowVal] || UITypes.SingleLineText |
|
|
|
const colData = jsonData.map((r: any) => extractNestedData(r, path)) |
|
Object.assign(column, this._getColumnUIDTAndMetas(colData, column.uidt)) |
|
columns.push(column) |
|
} |
|
|
|
return columns |
|
} |
|
|
|
_getColumnUIDTAndMetas(colData: any, defaultType: any) { |
|
const colProps = { uidt: defaultType } |
|
// todo: optimize |
|
if (colProps.uidt === UITypes.SingleLineText) { |
|
// check for long text |
|
if (isMultiLineTextType(colData)) { |
|
colProps.uidt = UITypes.LongText |
|
} |
|
if (isEmailType(colData)) { |
|
colProps.uidt = UITypes.Email |
|
} |
|
if (isUrlType(colData)) { |
|
colProps.uidt = UITypes.URL |
|
} else { |
|
const checkboxType = isCheckboxType(colData) |
|
if (checkboxType.length === 1) { |
|
colProps.uidt = UITypes.Checkbox |
|
} else { |
|
Object.assign(colProps, extractMultiOrSingleSelectProps(colData)) |
|
} |
|
} |
|
} else if (colProps.uidt === UITypes.Number) { |
|
if (isDecimalType(colData)) { |
|
colProps.uidt = UITypes.Decimal |
|
} |
|
} |
|
return colProps |
|
} |
|
|
|
_parseTableData(tableMeta: any) { |
|
for (const row of this.jsonData as any) { |
|
const rowData: any = {} |
|
for (let i = 0; i < tableMeta.columns.length; i++) { |
|
const value = extractNestedData(row, tableMeta.columns[i].path || []) |
|
if (tableMeta.columns[i].uidt === UITypes.Checkbox) { |
|
rowData[tableMeta.columns[i].ref_column_name] = getCheckboxValue(value) |
|
} else if (tableMeta.columns[i].uidt === UITypes.SingleSelect || tableMeta.columns[i].uidt === UITypes.MultiSelect) { |
|
rowData[tableMeta.columns[i].ref_column_name] = (value || '').toString().trim() || null |
|
} else if (tableMeta.columns[i].uidt === UITypes.JSON) { |
|
rowData[tableMeta.columns[i].ref_column_name] = JSON.stringify(value) |
|
} else { |
|
// toto: do parsing if necessary based on type |
|
rowData[tableMeta.columns[i].column_name] = value |
|
} |
|
} |
|
this.data[tableMeta.ref_table_name].push(rowData) |
|
} |
|
} |
|
}
|
|
|