mirror of https://github.com/nocodb/nocodb
Browse Source
Signed-off-by: Pranav C <pranavxc@gmail.com> # Conflicts: # packages/nc-gui/plugins/tele.jspull/1668/head
Pranav C
3 years ago
47 changed files with 3178 additions and 2766 deletions
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,44 @@
|
||||
import { Request, Response, Router } from 'express'; |
||||
import ncMetaAclMw from '../../helpers/ncMetaAclMw'; |
||||
import { |
||||
extractCsvData, |
||||
getViewAndModelFromRequestByAliasOrId |
||||
} from './helpers'; |
||||
import papaparse from 'papaparse'; |
||||
|
||||
async function csvDataExport(req: Request, res: Response) { |
||||
const { model, view } = await getViewAndModelFromRequestByAliasOrId(req); |
||||
|
||||
const { offset, csvRows, elapsed } = await extractCsvData(model, view, req); |
||||
|
||||
const data = papaparse.unparse( |
||||
{ |
||||
fields: model.columns.map(c => c.title), |
||||
data: csvRows |
||||
}, |
||||
{ |
||||
escapeFormulae: true |
||||
} |
||||
); |
||||
|
||||
res.set({ |
||||
'Access-Control-Expose-Headers': 'nc-export-offset', |
||||
'nc-export-offset': offset, |
||||
'nc-export-elapsed-time': elapsed, |
||||
'Content-Disposition': `attachment; filename="${view.title}-export.csv"` |
||||
}); |
||||
res.send(data); |
||||
} |
||||
|
||||
const router = Router({ mergeParams: true }); |
||||
|
||||
router.get( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/export/csv', |
||||
ncMetaAclMw(csvDataExport, 'csvDataExport') |
||||
); |
||||
router.get( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/views/:viewName/export/csv', |
||||
ncMetaAclMw(csvDataExport, 'csvDataExport') |
||||
); |
||||
|
||||
export default router; |
@ -0,0 +1,308 @@
|
||||
import { Request, Response, Router } from 'express'; |
||||
import Model from '../../../../noco-models/Model'; |
||||
import Base from '../../../../noco-models/Base'; |
||||
import NcConnectionMgrv2 from '../../../common/NcConnectionMgrv2'; |
||||
import { PagedResponseImpl } from '../../helpers/PagedResponse'; |
||||
import View from '../../../../noco-models/View'; |
||||
import ncMetaAclMw from '../../helpers/ncMetaAclMw'; |
||||
import { getViewAndModelFromRequestByAliasOrId } from './helpers'; |
||||
import { NcError } from '../../helpers/catchError'; |
||||
|
||||
export async function mmList(req: Request, res: Response, next) { |
||||
const { model, view } = await getViewAndModelFromRequestByAliasOrId(req); |
||||
|
||||
if (!model) return next(new Error('Table not found')); |
||||
|
||||
const base = await Base.get(model.base_id); |
||||
|
||||
const baseModel = await Model.getBaseModelSQL({ |
||||
id: model.id, |
||||
viewId: view?.id, |
||||
dbDriver: NcConnectionMgrv2.get(base) |
||||
}); |
||||
|
||||
const column = await getColumnByIdOrName(req.params.columnName, model); |
||||
|
||||
const data = await baseModel.mmList( |
||||
{ |
||||
colId: column.id, |
||||
parentId: req.params.rowId |
||||
}, |
||||
req.query as any |
||||
); |
||||
const count: any = await baseModel.mmListCount({ |
||||
colId: column.id, |
||||
parentId: req.params.rowId |
||||
}); |
||||
|
||||
res.json( |
||||
new PagedResponseImpl(data, { |
||||
count, |
||||
...req.query |
||||
}) |
||||
); |
||||
} |
||||
|
||||
export async function mmExcludedList(req: Request, res: Response, next) { |
||||
const view = await View.get(req.params.viewId); |
||||
|
||||
const model = await Model.getByIdOrName({ |
||||
id: view?.fk_model_id || req.params.viewId |
||||
}); |
||||
|
||||
if (!model) return next(new Error('Table not found')); |
||||
|
||||
const base = await Base.get(model.base_id); |
||||
|
||||
const baseModel = await Model.getBaseModelSQL({ |
||||
id: model.id, |
||||
viewId: view?.id, |
||||
dbDriver: NcConnectionMgrv2.get(base) |
||||
}); |
||||
const column = await getColumnByIdOrName(req.params.columnName, model); |
||||
|
||||
const data = await baseModel.getMmChildrenExcludedList( |
||||
{ |
||||
colId: column.id, |
||||
pid: req.params.rowId |
||||
}, |
||||
req.query |
||||
); |
||||
|
||||
const count = await baseModel.getMmChildrenExcludedListCount( |
||||
{ |
||||
colId: column.id, |
||||
pid: req.params.rowId |
||||
}, |
||||
req.query |
||||
); |
||||
|
||||
res.json( |
||||
new PagedResponseImpl(data, { |
||||
count, |
||||
...req.query |
||||
}) |
||||
); |
||||
} |
||||
|
||||
export async function hmExcludedList(req: Request, res: Response, next) { |
||||
const view = await View.get(req.params.viewId); |
||||
|
||||
const model = await Model.getByIdOrName({ |
||||
id: view?.fk_model_id || req.params.viewId |
||||
}); |
||||
|
||||
if (!model) return next(new Error('Table not found')); |
||||
|
||||
const base = await Base.get(model.base_id); |
||||
|
||||
const baseModel = await Model.getBaseModelSQL({ |
||||
id: model.id, |
||||
viewId: view?.id, |
||||
dbDriver: NcConnectionMgrv2.get(base) |
||||
}); |
||||
|
||||
const column = await getColumnByIdOrName(req.params.columnName, model); |
||||
|
||||
const data = await baseModel.getHmChildrenExcludedList( |
||||
{ |
||||
colId: column.id, |
||||
pid: req.params.rowId |
||||
}, |
||||
req.query |
||||
); |
||||
|
||||
const count = await baseModel.getHmChildrenExcludedListCount( |
||||
{ |
||||
colId: column.id, |
||||
pid: req.params.rowId |
||||
}, |
||||
req.query |
||||
); |
||||
|
||||
res.json( |
||||
new PagedResponseImpl(data, { |
||||
count, |
||||
...req.query |
||||
}) |
||||
); |
||||
} |
||||
|
||||
export async function btExcludedList(req: Request, res: Response, next) { |
||||
const view = await View.get(req.params.viewId); |
||||
|
||||
const model = await Model.getByIdOrName({ |
||||
id: view?.fk_model_id || req.params.viewId |
||||
}); |
||||
|
||||
if (!model) return next(new Error('Table not found')); |
||||
|
||||
const base = await Base.get(model.base_id); |
||||
|
||||
const baseModel = await Model.getBaseModelSQL({ |
||||
id: model.id, |
||||
viewId: view?.id, |
||||
dbDriver: NcConnectionMgrv2.get(base) |
||||
}); |
||||
|
||||
const column = await getColumnByIdOrName(req.params.columnName, model); |
||||
|
||||
const data = await baseModel.getBtChildrenExcludedList( |
||||
{ |
||||
colId: column.id, |
||||
cid: req.params.rowId |
||||
}, |
||||
req.query |
||||
); |
||||
|
||||
const count = await baseModel.getBtChildrenExcludedListCount( |
||||
{ |
||||
colId: column.id, |
||||
cid: req.params.rowId |
||||
}, |
||||
req.query |
||||
); |
||||
|
||||
res.json( |
||||
new PagedResponseImpl(data, { |
||||
count, |
||||
...req.query |
||||
}) |
||||
); |
||||
} |
||||
|
||||
export async function hmList(req: Request, res: Response, next) { |
||||
const view = await View.get(req.params.viewId); |
||||
|
||||
const model = await Model.getByIdOrName({ |
||||
id: view?.fk_model_id || req.params.viewId |
||||
}); |
||||
|
||||
if (!model) return next(new Error('Table not found')); |
||||
|
||||
const base = await Base.get(model.base_id); |
||||
|
||||
const baseModel = await Model.getBaseModelSQL({ |
||||
id: model.id, |
||||
viewId: view?.id, |
||||
dbDriver: NcConnectionMgrv2.get(base) |
||||
}); |
||||
|
||||
const column = await getColumnByIdOrName(req.params.columnName, model); |
||||
|
||||
const data = await baseModel.hmList( |
||||
{ |
||||
colId: column.id, |
||||
id: req.params.rowId |
||||
}, |
||||
req.query |
||||
); |
||||
|
||||
const count = await baseModel.hmListCount({ |
||||
colId: column.id, |
||||
id: req.params.rowId |
||||
}); |
||||
|
||||
res.json( |
||||
new PagedResponseImpl(data, { |
||||
totalRows: count |
||||
} as any) |
||||
); |
||||
} |
||||
|
||||
//@ts-ignore
|
||||
async function relationDataDelete(req, res) { |
||||
const view = await View.get(req.params.viewId); |
||||
|
||||
const model = await Model.getByIdOrName({ |
||||
id: view?.fk_model_id || req.params.viewId |
||||
}); |
||||
|
||||
if (!model) NcError.notFound('Table not found'); |
||||
|
||||
const base = await Base.get(model.base_id); |
||||
|
||||
const baseModel = await Model.getBaseModelSQL({ |
||||
id: model.id, |
||||
viewId: view?.id, |
||||
dbDriver: NcConnectionMgrv2.get(base) |
||||
}); |
||||
|
||||
const column = await getColumnByIdOrName(req.params.columnName, model); |
||||
|
||||
await baseModel.removeChild({ |
||||
colId: column.id, |
||||
childId: req.params.childId, |
||||
rowId: req.params.rowId |
||||
}); |
||||
|
||||
res.json({ msg: 'success' }); |
||||
} |
||||
|
||||
//@ts-ignore
|
||||
async function relationDataAdd(req, res) { |
||||
const view = await View.get(req.params.viewId); |
||||
|
||||
const model = await Model.getByIdOrName({ |
||||
id: view?.fk_model_id || req.params.viewId |
||||
}); |
||||
|
||||
if (!model) NcError.notFound('Table not found'); |
||||
|
||||
const base = await Base.get(model.base_id); |
||||
|
||||
const baseModel = await Model.getBaseModelSQL({ |
||||
id: model.id, |
||||
viewId: view?.id, |
||||
dbDriver: NcConnectionMgrv2.get(base) |
||||
}); |
||||
|
||||
const column = await getColumnByIdOrName(req.params.columnName, model); |
||||
await baseModel.addChild({ |
||||
colId: column.id, |
||||
childId: req.params.childId, |
||||
rowId: req.params.rowId |
||||
}); |
||||
|
||||
res.json({ msg: 'success' }); |
||||
} |
||||
|
||||
async function getColumnByIdOrName(columnNameOrId: string, model: Model) { |
||||
const column = (await model.getColumns()).find( |
||||
c => |
||||
column.title === columnNameOrId || |
||||
c.id === columnNameOrId || |
||||
column.column_name === columnNameOrId |
||||
); |
||||
|
||||
if (!column) |
||||
NcError.notFound(`Column with id/name '${columnNameOrId}' is not found`); |
||||
|
||||
return column; |
||||
} |
||||
|
||||
const router = Router({ mergeParams: true }); |
||||
|
||||
router.get( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/mm/:columnName/exclude', |
||||
ncMetaAclMw(mmExcludedList, 'mmExcludedList') |
||||
); |
||||
router.get( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/hm/:columnName/exclude', |
||||
ncMetaAclMw(hmExcludedList, 'hmExcludedList') |
||||
); |
||||
router.get( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/bt/:columnName/exclude', |
||||
ncMetaAclMw(btExcludedList, 'btExcludedList') |
||||
); |
||||
|
||||
router.post( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/:relationType/:columnName/:refRowId', |
||||
ncMetaAclMw(relationDataAdd, 'relationDataAdd') |
||||
); |
||||
router.delete( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/:relationType/:columnName/:refRowId', |
||||
ncMetaAclMw(relationDataDelete, 'relationDataDelete') |
||||
); |
||||
|
||||
export default router; |
@ -0,0 +1,155 @@
|
||||
import Project from '../../../../noco-models/Project'; |
||||
import Model from '../../../../noco-models/Model'; |
||||
import View from '../../../../noco-models/View'; |
||||
import { NcError } from '../../helpers/catchError'; |
||||
import { Request } from 'express'; |
||||
import Base from '../../../../noco-models/Base'; |
||||
import NcConnectionMgrv2 from '../../../common/NcConnectionMgrv2'; |
||||
import { isSystemColumn, UITypes } from 'nocodb-sdk'; |
||||
|
||||
import { nocoExecute } from 'nc-help'; |
||||
import Column from '../../../../noco-models/Column'; |
||||
import LookupColumn from '../../../../noco-models/LookupColumn'; |
||||
import LinkToAnotherRecordColumn from '../../../../noco-models/LinkToAnotherRecordColumn'; |
||||
|
||||
export async function getViewAndModelFromRequestByAliasOrId( |
||||
req: |
||||
| Request<{ projectName: string; tableName: string; viewName?: string }> |
||||
| Request |
||||
) { |
||||
let project = await Project.getWithInfoByTitle(req.params.projectName); |
||||
|
||||
if (!project) { |
||||
project = await Project.getWithInfo(req.params.projectName); |
||||
} |
||||
|
||||
const model = await Model.getByAliasOrId({ |
||||
project_id: project.id, |
||||
base_id: project.bases?.[0]?.id, |
||||
aliasOrId: req.params.tableName |
||||
}); |
||||
const view = |
||||
req.params.viewName && |
||||
(await View.getByTitleOrId({ |
||||
titleOrId: req.params.viewName, |
||||
fk_model_id: model.id |
||||
})); |
||||
if (!model) NcError.notFound('Table not found'); |
||||
return { model, view }; |
||||
} |
||||
|
||||
export async function extractCsvData(model: Model, view: View, req: Request) { |
||||
const base = await Base.get(model.base_id); |
||||
const baseModel = await Model.getBaseModelSQL({ |
||||
id: model.id, |
||||
viewId: view?.id, |
||||
dbDriver: NcConnectionMgrv2.get(base) |
||||
}); |
||||
|
||||
let offset = +req.query.offset || 0; |
||||
const limit = 100; |
||||
// const size = +process.env.NC_EXPORT_MAX_SIZE || 1024;
|
||||
const timeout = +process.env.NC_EXPORT_MAX_TIMEOUT || 5000; |
||||
const csvRows = []; |
||||
const startTime = process.hrtime(); |
||||
let elapsed, temp; |
||||
|
||||
for ( |
||||
elapsed = 0; |
||||
elapsed < timeout; |
||||
offset += limit, |
||||
temp = process.hrtime(startTime), |
||||
elapsed = temp[0] * 1000 + temp[1] / 1000000 |
||||
) { |
||||
const rows = await nocoExecute( |
||||
await baseModel.defaultResolverReq(req.query, false, false), |
||||
await baseModel.list({ ...req.query, offset, limit }), |
||||
{}, |
||||
req.query |
||||
); |
||||
|
||||
if (!rows?.length) { |
||||
offset = -1; |
||||
break; |
||||
} |
||||
|
||||
for (const row of rows) { |
||||
const csvRow = { ...row }; |
||||
|
||||
for (const column of view.model.columns) { |
||||
if (isSystemColumn(column) && !view.show_system_fields) continue; |
||||
csvRow[column.title] = await serializeCellValue({ |
||||
value: row[column.title], |
||||
column |
||||
}); |
||||
} |
||||
csvRows.push(csvRow); |
||||
} |
||||
} |
||||
return { offset, csvRows, elapsed }; |
||||
} |
||||
|
||||
export async function serializeCellValue({ |
||||
value, |
||||
column |
||||
}: { |
||||
column?: Column; |
||||
value: any; |
||||
}) { |
||||
if (!column) { |
||||
return value; |
||||
} |
||||
|
||||
if (!value) return value; |
||||
|
||||
switch (column?.uidt) { |
||||
case UITypes.Attachment: { |
||||
let data = value; |
||||
try { |
||||
if (typeof value === 'string') { |
||||
data = JSON.parse(value); |
||||
} |
||||
} catch {} |
||||
|
||||
return (data || []).map( |
||||
attachment => |
||||
`${encodeURI(attachment.title)}(${encodeURI(attachment.url)})` |
||||
); |
||||
} |
||||
case UITypes.Lookup: |
||||
{ |
||||
const colOptions = await column.getColOptions<LookupColumn>(); |
||||
const lookupColumn = await colOptions.getLookupColumn(); |
||||
return ( |
||||
await Promise.all( |
||||
[...(Array.isArray(value) ? value : [value])].map(async v => |
||||
serializeCellValue({ |
||||
value: v, |
||||
column: lookupColumn |
||||
}) |
||||
) |
||||
) |
||||
).join(', '); |
||||
} |
||||
break; |
||||
case UITypes.LinkToAnotherRecord: |
||||
{ |
||||
const colOptions = await column.getColOptions< |
||||
LinkToAnotherRecordColumn |
||||
>(); |
||||
const relatedModel = await colOptions.getRelatedTable(); |
||||
await relatedModel.getColumns(); |
||||
return [...(Array.isArray(value) ? value : [value])] |
||||
.map(v => { |
||||
return v[relatedModel.primaryValue?.title]; |
||||
}) |
||||
.join(', '); |
||||
} |
||||
break; |
||||
default: |
||||
if (value && typeof value === 'object') { |
||||
return JSON.stringify(value); |
||||
} |
||||
return value; |
||||
} |
||||
} |
Loading…
Reference in new issue