From f1159747df9b225c57c949b84e60e9522b157fde Mon Sep 17 00:00:00 2001 From: Raju Udava <86527202+dstala@users.noreply.github.com> Date: Mon, 23 May 2022 18:42:44 +0530 Subject: [PATCH] base files Signed-off-by: Raju Udava <86527202+dstala@users.noreply.github.com> --- .../tests/export-import/exportSchema.js | 202 ++++++++++++++++++ .../tests/export-import/importSchema.js | 173 +++++++++++++++ 2 files changed, 375 insertions(+) create mode 100644 packages/nocodb/tests/export-import/exportSchema.js create mode 100644 packages/nocodb/tests/export-import/importSchema.js diff --git a/packages/nocodb/tests/export-import/exportSchema.js b/packages/nocodb/tests/export-import/exportSchema.js new file mode 100644 index 0000000000..d55abf7ef9 --- /dev/null +++ b/packages/nocodb/tests/export-import/exportSchema.js @@ -0,0 +1,202 @@ +const Api = require('nocodb-sdk').Api; +const { UITypes } = require('nocodb-sdk'); +const jsonfile = require("jsonfile"); + +let ncMap = {} +let tblSchema = [] +let api = {} +let viewStore = {} + +const ncConfig = { + projectName: "x", + baseURL: "http://localhost:8080", + headers: { + 'xc-auth': "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6InVzZXJAbm9jb2RiLmNvbSIsImZpcnN0bmFtZSI6bnVsbCwibGFzdG5hbWUiOm51bGwsImlkIjoidXNfaGJ1aDFmMTNmemc4dTEiLCJyb2xlcyI6InVzZXIsc3VwZXIiLCJpYXQiOjE2NTMwNTU5MzR9.nADVbCbSE0WEbPrpKuq_dlMHrrxieQurYPiOIU2Gf4k" + } +} + +// helper routines +// remove objects containing 0/ false/ null +// fixme: how to handle when cdf (default value) is configured as 0/ null/ false +function removeEmpty(obj) { + return Object.fromEntries( + Object.entries(obj) + .filter(([_, v]) => ((v != null) && (v != 0) && (v != false))) + .map(([k, v]) => [k, v === Object(v) ? removeEmpty(v) : v]) + ); +} + +// let linksAdded = [] +// function isLinkExists(pId, cId) { +// let idx = linksAdded.findIndex(a => a.child === pId && a.parent === cId) +// if(idx === -1) { +// linksAdded.push({ child: cId, parent: pId }) +// return false; +// } +// return true; +// } + +function ncGetColData(cId) { + +} + +function addColumnSpecificData(c) { + // pick required fields to proceed further + let col = removeEmpty((({ id, title, column_name, uidt, dt, pk, pv, rqd, dtxp, system }) => + ({ id, title, column_name, uidt, dt, pk, pv, rqd, dtxp, system }))(c)) + + let colOptions = null + switch(c.uidt) { + case UITypes.Formula: + colOptions = { + formula: c.colOptions.formula, + formula_raw: c.colOptions.formula_raw + } + break + case UITypes.LinkToAnotherRecord: + colOptions = { + fk_model_id: c.fk_model_id, + fk_related_model_id: c.colOptions.fk_related_model_id, + fk_child_column_id: c.colOptions.fk_child_column_id, + fk_parent_column_id: c.colOptions.fk_parent_column_id, + type: c.colOptions.type + } + break; + case UITypes.Lookup: + colOptions = { + fk_model_id: c.fk_model_id, + fk_relation_column_id: c.colOptions.fk_relation_column_id, + fk_lookup_column_id: c.colOptions.fk_lookup_column_id, + } + break; + case UITypes.Rollup: + colOptions = { + fk_model_id: c.fk_model_id, + fk_relation_column_id: c.colOptions.fk_relation_column_id, + fk_rollup_column_id: c.colOptions.fk_rollup_column_id, + rollup_function: c.colOptions.rollup_function + } + break; + } + + // colOptions not required for formula + if (c.uidt === UITypes.Formula) { + col.formula = c.colOptions.formula; + col.formula_raw = c.colOptions.formula_raw; + } + else if(colOptions) col[`colOptions`] = colOptions; + return col; +} + +function addViewDetails(v) { + // pick required fields to proceed further + let view = (({ id, title, type, show_system_fields, lock_type, order }) => ( + { id, title, type, show_system_fields, lock_type, order }))(v); + + // form view + if(v.type === 1) { + view.property = (({ heading, subheading, success_msg, redirect_after_secs, email, submit_another_form, show_blank_form }) => ( + { heading, subheading, success_msg, redirect_after_secs, email, submit_another_form, show_blank_form }))(v.view); + } + + // gallery view + else if (v.type === 2) { + view.property = {fk_cover_image_col_id: ncMap[v.view.fk_cover_image_col_id]} + } + + // gallery view doesn't share column information in api yet + if(v.type !== 2) { + view.columns = viewStore[v.id].map(a => (({ id, width, order, show }) => ( + { id, width, order, show }))(a)) + for (let i = 0; i < view.columns?.length; i++) + view.columns[i].title = ncMap[viewStore[v.id][i].id] + + view.columns = view.columns.filter(a => a.title.includes('_nc_m2m_') === false) + } + return view; +} + +// view data stored as is for quick access +async function storeViewDetails(tableId) { + // read view data for each table + let viewList = await api.dbView.list(tableId) + for(let j=0; j name + ncMap[tblId] = tbl.title; + + // column ID <> name + tbl.columns.map(x => ncMap[x.id] = x.title ) + + // view ID <> name + tbl.views.map(x => ncMap[x.id] = x.tn ) + + for(let i=0; i name + viewColumns?.map(a => ncMap[a.id] = ncMap[a.fk_column_id]) + } + } +} + +// main +// +async function exportSchema() { + api = new Api( ncConfig ); + + // fetch project details (id et.al) + const x = await api.project.list(); + const p = x.list.find(a => a.title === ncConfig.projectName); + + await generateMapTbl(p.id) + + // read project + const tblList = await api.dbTable.list(p.id) + for(let i=0; i addColumnSpecificData(c))] + .filter(a => a.title.includes('_nc_m2m_') === false) + .filter(a => !(a?.system===1 && a.uidt===UITypes.LinkToAnotherRecord )), + views: [...tbl.views.map(v => addViewDetails(v))] + } + tblSchema.push(tSchema) + } +} + +(async() => { + await exportSchema() + jsonfile.writeFileSync(`${ncConfig.projectName.replace(/ /g, '_')}.json`, tblSchema, { spaces: 2 }) +})().catch(e => {console.log(e)}) diff --git a/packages/nocodb/tests/export-import/importSchema.js b/packages/nocodb/tests/export-import/importSchema.js new file mode 100644 index 0000000000..f2b200d098 --- /dev/null +++ b/packages/nocodb/tests/export-import/importSchema.js @@ -0,0 +1,173 @@ +const Api = require('nocodb-sdk').Api; +const { UITypes } = require('nocodb-sdk'); +const jsonfile = require("jsonfile"); + +let api = {} +let ncIn = jsonfile.readFileSync('x.json') +let ncProject = {} +let link = [] +let lookup = [] +let rollup = [] +let formula = [] +let ncTables = {} + +const ncConfig = { + projectName: "x2", + baseURL: "http://localhost:8080", + headers: { + 'xc-auth': "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6InVzZXJAbm9jb2RiLmNvbSIsImZpcnN0bmFtZSI6bnVsbCwibGFzdG5hbWUiOm51bGwsImlkIjoidXNfaGJ1aDFmMTNmemc4dTEiLCJyb2xlcyI6InVzZXIsc3VwZXIiLCJpYXQiOjE2NTMwNTU5MzR9.nADVbCbSE0WEbPrpKuq_dlMHrrxieQurYPiOIU2Gf4k" + } +} + +async function createBaseTables() { + for(let i=0; i a.uidt !== UITypes.LinkToAnotherRecord && a.uidt !== UITypes.Lookup && a.uidt !== UITypes.Formula); + link.push(...tblSchema.columns.filter(a => a.uidt === UITypes.LinkToAnotherRecord)) + lookup.push(...tblSchema.columns.filter(a => a.uidt === UITypes.Lookup)) + rollup.push(...tblSchema.columns.filter(a => a.uidt === UITypes.Rollup)) + formula.push(...tblSchema.columns.filter(a => a.uidt === UITypes.Formula)) + + let tbl = await api.dbTable.create(ncProject.id, { + title: tblSchema.title, + table_name: tblSchema.title, + columns: reducedColumnSet.map(({id,...rest}) => ({...rest})) + }) + ncTables[tbl.title] = tbl; + ncTables[tbl.id] = tbl; + ncTables[tblSchema.id] = tbl; + } +} + +let linksCreated = [] +function isLinkCreated(pId, cId) { + let idx = linksCreated.findIndex(a => a.cId === pId && a.pId === cId) + if(idx === -1) { + linksCreated.push({pId: pId, cId: cId}) + return false; + } + return true; +} + +async function createFormula() { + for (let i = 0; i < formula.length; i++) { + let tbl = await api.dbTableColumn.create(srcTbl.id, { + uidt: UITypes.LinkToAnotherRecord, + title: link[i].title, + parentId: srcTbl.id, + childId: dstTbl.id, + type: link[i].colOptions.type + }); + } +} + +async function createLinks() { + for (let i = 0; i < link.length; i++) { + if (((link[i].colOptions.type === 'mm') && + (false === isLinkCreated(link[i].colOptions.fk_parent_column_id, link[i].colOptions.fk_child_column_id))) + || (link[i].colOptions.type === 'hm')) { + let srcTbl = ncTables[link[i].colOptions.fk_model_id]; + let dstTbl = ncTables[link[i].colOptions.fk_related_model_id]; + // create link + let tbl = await api.dbTableColumn.create(srcTbl.id, { + uidt: UITypes.LinkToAnotherRecord, + title: link[i].title, + parentId: srcTbl.id, + childId: dstTbl.id, + type: link[i].colOptions.type + }); + ncTables[tbl.title] = tbl; + ncTables[tbl.id] = tbl; + ncTables[link[i].colOptions.fk_model_id] = tbl; + + let v2ColSchema = tbl.columns.find(x => x.title === link[i].title) + + // read related table again after link is created + dstTbl = await api.dbTable.read(dstTbl.id); + let v2SymmetricColumn = (link[i].colOptions.type === 'mm') ? dstTbl.columns.find(x => x.uidt === UITypes.LinkToAnotherRecord && x?.colOptions.fk_parent_column_id === v2ColSchema.colOptions.fk_child_column_id && x?.colOptions.fk_child_column_id === v2ColSchema.colOptions.fk_parent_column_id) : + dstTbl.columns.find(x => x.uidt === UITypes.LinkToAnotherRecord && x?.colOptions.fk_parent_column_id === v2ColSchema.colOptions.fk_parent_column_id && x?.colOptions.fk_child_column_id === v2ColSchema.colOptions.fk_child_column_id) + let v1SymmetricColumn = (link[i].colOptions.type === 'mm') ? link.find(x => x.colOptions.fk_parent_column_id === link[i].colOptions.fk_child_column_id && x.colOptions.fk_child_column_id === link[i].colOptions.fk_parent_column_id) : + link.find(x => x.colOptions.fk_parent_column_id === link[i].colOptions.fk_parent_column_id && x.colOptions.fk_child_column_id === link[i].colOptions.fk_child_column_id); + + tbl = await api.dbTableColumn.update(v2SymmetricColumn.id, { + ...v2SymmetricColumn, + title: v1SymmetricColumn.title, + column_name: null + }) + ncTables[tbl.title] = tbl; + ncTables[tbl.id] = tbl; + ncTables[v1SymmetricColumn.colOptions.fk_model_id] = tbl; + } + } +} + +function get_v2Id(v1ColId) { + for(let i=0; i x.id === v1ColId))) { + let colName = colSchema.title; + let v2Tbl = ncTables[tblSchema.id]; + return v2Tbl.columns.find(y => y.title === colName)?.id + } + } +} + +async function createLookup() { + for(let i=0; i a.title === ncConfig.projectName); + if (p) await api.project.delete(p.id); + ncProject = await api.project.create({ title: ncConfig.projectName }) + await createBaseTables() + await createFormula() + await createLinks() + await createLookup() + await createRollup() +} +(async() => { + await importSchema() + console.log('completed') +})().catch(e => console.log(e)) \ No newline at end of file