From 7a5badcab5bd8497670dade74dad7c0b8edba9fd Mon Sep 17 00:00:00 2001 From: Pranav C Date: Sat, 4 Mar 2023 00:07:20 +0530 Subject: [PATCH] refactor: use service in public api handlers Signed-off-by: Pranav C --- .../src/lib/controllers/publicApis/index.ts | 12 +- .../controllers/publicApis/publicDataApis.ts | 449 ++---------------- .../controllers/publicApis/publicMetaApis.ts | 99 +--- 3 files changed, 61 insertions(+), 499 deletions(-) diff --git a/packages/nocodb/src/lib/controllers/publicApis/index.ts b/packages/nocodb/src/lib/controllers/publicApis/index.ts index 60028a421f..8e1c237b36 100644 --- a/packages/nocodb/src/lib/controllers/publicApis/index.ts +++ b/packages/nocodb/src/lib/controllers/publicApis/index.ts @@ -1,5 +1,9 @@ -import publicDataApis from './publicDataApis'; -import publicDataExportApis from './publicDataExportApis'; -import publicMetaApis from './publicMetaApis'; +import publicDataController from './publicDataApis'; +import publicDataExportController from './publicDataExportApis'; +import publicMetaController from './publicMetaApis'; -export { publicDataApis, publicDataExportApis, publicMetaApis }; +export { + publicDataController, + publicDataExportController, + publicMetaController, +}; diff --git a/packages/nocodb/src/lib/controllers/publicApis/publicDataApis.ts b/packages/nocodb/src/lib/controllers/publicApis/publicDataApis.ts index 792a6b4b61..46a73a0f2a 100644 --- a/packages/nocodb/src/lib/controllers/publicApis/publicDataApis.ts +++ b/packages/nocodb/src/lib/controllers/publicApis/publicDataApis.ts @@ -1,439 +1,72 @@ import { Request, Response, Router } from 'express'; -import Model from '../../models/Model'; -import { nocoExecute } from 'nc-help'; -import Base from '../../models/Base'; -import NcConnectionMgrv2 from '../../utils/common/NcConnectionMgrv2'; -import { PagedResponseImpl } from '../../meta/helpers/PagedResponse'; -import View from '../../models/View'; -import catchError, { NcError } from '../../meta/helpers/catchError'; import multer from 'multer'; -import { ErrorMessages, UITypes, ViewTypes } from 'nocodb-sdk'; -import Column from '../../models/Column'; -import LinkToAnotherRecordColumn from '../../models/LinkToAnotherRecordColumn'; -import NcPluginMgrv2 from '../../meta/helpers/NcPluginMgrv2'; -import path from 'path'; -import { nanoid } from 'nanoid'; -import { mimeIcons } from '../../utils/mimeTypes'; -import slash from 'slash'; -import { sanitizeUrlPath } from '../../services/attachmentService'; -import getAst from '../../db/sql-data-mapper/lib/sql/helpers/getAst'; -import { getColumnByIdOrName } from '../dataApis/helpers'; import { NC_ATTACHMENT_FIELD_SIZE } from '../../constants'; +import catchError from '../../meta/helpers/catchError'; +import { publicDataService } from '../../services'; export async function dataList(req: Request, res: Response) { - try { - const view = await View.getByUUID(req.params.sharedViewUuid); - - if (!view) NcError.notFound('Not found'); - if ( - view.type !== ViewTypes.GRID && - view.type !== ViewTypes.KANBAN && - view.type !== ViewTypes.GALLERY && - view.type !== ViewTypes.MAP - ) { - NcError.notFound('Not found'); - } - - if (view.password && view.password !== req.headers?.['xc-password']) { - return NcError.forbidden(ErrorMessages.INVALID_SHARED_VIEW_PASSWORD); - } - - const model = await Model.getByIdOrName({ - id: view?.fk_model_id, - }); - - const base = await Base.get(model.base_id); - - const baseModel = await Model.getBaseModelSQL({ - id: model.id, - viewId: view?.id, - dbDriver: NcConnectionMgrv2.get(base), - }); - - const listArgs: any = { ...req.query }; - try { - listArgs.filterArr = JSON.parse(listArgs.filterArrJson); - } catch (e) {} - try { - listArgs.sortArr = JSON.parse(listArgs.sortArrJson); - } catch (e) {} - - let data = []; - let count = 0; - - try { - data = await nocoExecute( - await getAst({ - query: req.query, - model, - view, - }), - await baseModel.list(listArgs), - {}, - listArgs - ); - count = await baseModel.count(listArgs); - } catch (e) { - // show empty result instead of throwing error here - // e.g. search some text in a numeric field - } - - res.json({ - data: new PagedResponseImpl(data, { ...req.query, count }), - }); - } catch (e) { - console.log(e); - res.status(500).json({ msg: e.message }); - } + const pagedResponse = await publicDataService.dataList({ + query: req.query, + password: req.headers?.['xc-password'] as string, + sharedViewUuid: req.params.sharedViewUuid, + }); + res.json({ data: pagedResponse }); } // todo: Handle the error case where view doesnt belong to model async function groupedDataList(req: Request, res: Response) { - try { - const view = await View.getByUUID(req.params.sharedViewUuid); - - if (!view) NcError.notFound('Not found'); - - if ( - view.type !== ViewTypes.GRID && - view.type !== ViewTypes.KANBAN && - view.type !== ViewTypes.GALLERY - ) { - NcError.notFound('Not found'); - } - - if (view.password && view.password !== req.headers?.['xc-password']) { - return NcError.forbidden(ErrorMessages.INVALID_SHARED_VIEW_PASSWORD); - } - - const model = await Model.getByIdOrName({ - id: view?.fk_model_id, - }); - - res.json(await getGroupedDataList(model, view, req)); - } catch (e) { - console.log(e); - res.status(500).json({ msg: e.message }); - } -} - -async function getGroupedDataList(model, view: View, req) { - const base = await Base.get(model.base_id); - - const baseModel = await Model.getBaseModelSQL({ - id: model.id, - viewId: view?.id, - dbDriver: NcConnectionMgrv2.get(base), + const groupedData = await publicDataService.groupedDataList({ + query: req.query, + password: req.headers?.['xc-password'] as string, + sharedViewUuid: req.params.sharedViewUuid, + groupColumnId: req.params.columnId, }); - - const requestObj = await getAst({ model, query: req.query, view }); - - const listArgs: any = { ...req.query }; - try { - listArgs.filterArr = JSON.parse(listArgs.filterArrJson); - } catch (e) {} - try { - listArgs.sortArr = JSON.parse(listArgs.sortArrJson); - } catch (e) {} - try { - listArgs.options = JSON.parse(listArgs.optionsArrJson); - } catch (e) {} - - let data = []; - - try { - const groupedData = await baseModel.groupedList({ - ...listArgs, - groupColumnId: req.params.columnId, - }); - data = await nocoExecute( - { key: 1, value: requestObj }, - groupedData, - {}, - listArgs - ); - const countArr = await baseModel.groupedListCount({ - ...listArgs, - groupColumnId: req.params.columnId, - }); - data = data.map((item) => { - // todo: use map to avoid loop - const count = - countArr.find((countItem: any) => countItem.key === item.key)?.count ?? - 0; - - item.value = new PagedResponseImpl(item.value, { - ...req.query, - count: count, - }); - return item; - }); - } catch (e) { - // show empty result instead of throwing error here - // e.g. search some text in a numeric field - } - return data; + res.json(groupedData); } -async function dataInsert( - req: Request & { files: any[] }, - res: Response, - next -) { - const view = await View.getByUUID(req.params.sharedViewUuid); - - if (!view) return next(new Error('Not found')); - if (view.type !== ViewTypes.FORM) return next(new Error('Not found')); - - if (view.password && view.password !== req.headers?.['xc-password']) { - return res.status(403).json(ErrorMessages.INVALID_SHARED_VIEW_PASSWORD); - } - - const model = await Model.getByIdOrName({ - id: view?.fk_model_id, +async function dataInsert(req: Request & { files: any[] }, res: Response) { + const insertResult = await publicDataService.dataInsert({ + sharedViewUuid: req.params.sharedViewUuid, + password: req.headers?.['xc-password'] as string, + body: req.body?.data, + siteUrl: (req as any).ncSiteUrl, + files: req.files, }); - const base = await Base.get(model.base_id); - const project = await base.getProject(); - - const baseModel = await Model.getBaseModelSQL({ - id: model.id, - viewId: view?.id, - dbDriver: NcConnectionMgrv2.get(base), - }); - - await view.getViewWithInfo(); - await view.getColumns(); - await view.getModelWithInfo(); - await view.model.getColumns(); - - const fields = (view.model.columns = view.columns - .filter((c) => c.show) - .reduce((o, c) => { - o[view.model.columnsById[c.fk_column_id].title] = new Column({ - ...c, - ...view.model.columnsById[c.fk_column_id], - } as any); - return o; - }, {}) as any); - - let body = req.body?.data; - - if (typeof body === 'string') body = JSON.parse(body); - - const insertObject = Object.entries(body).reduce((obj, [key, val]) => { - if (key in fields) { - obj[key] = val; - } - return obj; - }, {}); - - const attachments = {}; - const storageAdapter = await NcPluginMgrv2.storageAdapter(); - - for (const file of req.files || []) { - // remove `_` prefix and `[]` suffix - const fieldName = file?.fieldname?.replace(/^_|\[\d*]$/g, ''); - - const filePath = sanitizeUrlPath([ - 'v1', - project.title, - model.title, - fieldName, - ]); - if (fieldName in fields && fields[fieldName].uidt === UITypes.Attachment) { - attachments[fieldName] = attachments[fieldName] || []; - const fileName = `${nanoid(6)}_${file.originalname}`; - let url = await storageAdapter.fileCreate( - slash(path.join('nc', 'uploads', ...filePath, fileName)), - file - ); - - if (!url) { - url = `${(req as any).ncSiteUrl}/download/${filePath.join( - '/' - )}/${fileName}`; - } - - attachments[fieldName].push({ - url, - title: file.originalname, - mimetype: file.mimetype, - size: file.size, - icon: mimeIcons[path.extname(file.originalname).slice(1)] || undefined, - }); - } - } - - for (const [column, data] of Object.entries(attachments)) { - insertObject[column] = JSON.stringify(data); - } - - res.json(await baseModel.nestedInsert(insertObject, null)); + res.json(insertResult); } async function relDataList(req, res) { - const view = await View.getByUUID(req.params.sharedViewUuid); - - if (!view) NcError.notFound('Not found'); - if (view.type !== ViewTypes.FORM) NcError.notFound('Not found'); - - if (view.password && view.password !== req.headers?.['xc-password']) { - NcError.forbidden(ErrorMessages.INVALID_SHARED_VIEW_PASSWORD); - } - - const column = await Column.get({ colId: req.params.columnId }); - const colOptions = await column.getColOptions(); - - const model = await colOptions.getRelatedTable(); - - const base = await Base.get(model.base_id); - - const baseModel = await Model.getBaseModelSQL({ - id: model.id, - viewId: view?.id, - dbDriver: NcConnectionMgrv2.get(base), - }); - - const requestObj = await getAst({ + const pagedResponse = await publicDataService.relDataList({ query: req.query, - model, - extractOnlyPrimaries: true, + password: req.headers?.['xc-password'] as string, + sharedViewUuid: req.params.sharedViewUuid, + columnId: req.params.columnId, }); - let data = []; - let count = 0; - try { - data = data = await nocoExecute( - requestObj, - await baseModel.list(req.query), - {}, - req.query - ); - count = await baseModel.count(req.query); - } catch (e) { - // show empty result instead of throwing error here - // e.g. search some text in a numeric field - } - - res.json(new PagedResponseImpl(data, { ...req.query, count })); + res.json(pagedResponse); } export async function publicMmList(req: Request, res: Response) { - const view = await View.getByUUID(req.params.sharedViewUuid); - - if (!view) NcError.notFound('Not found'); - if (view.type !== ViewTypes.GRID) NcError.notFound('Not found'); - - if (view.password && view.password !== req.headers?.['xc-password']) { - NcError.forbidden(ErrorMessages.INVALID_SHARED_VIEW_PASSWORD); - } - - const column = await getColumnByIdOrName( - req.params.colId, - await view.getModel() - ); - - if (column.fk_model_id !== view.fk_model_id) - NcError.badRequest("Column doesn't belongs to the model"); - - const base = await Base.get(view.base_id); - - const baseModel = await Model.getBaseModelSQL({ - id: view.fk_model_id, - viewId: view?.id, - dbDriver: NcConnectionMgrv2.get(base), - }); - - const key = `List`; - const requestObj: any = { - [key]: 1, - }; - - const data = ( - await nocoExecute( - requestObj, - { - [key]: async (args) => { - return await baseModel.mmList( - { - colId: req.params.colId, - parentId: req.params.rowId, - }, - args - ); - }, - }, - {}, - - { nested: { [key]: req.query } } - ) - )?.[key]; - - const count: any = await baseModel.mmListCount({ - colId: req.params.colId, - parentId: req.params.rowId, + const paginatedResponse = await publicDataService.publicMmList({ + query: req.query, + password: req.headers?.['xc-password'] as string, + sharedViewUuid: req.params.sharedViewUuid, + columnId: req.params.columnId, + rowId: req.params.rowId, }); - - res.json(new PagedResponseImpl(data, { ...req.query, count })); + res.json(paginatedResponse); } export async function publicHmList(req: Request, res: Response) { - const view = await View.getByUUID(req.params.sharedViewUuid); - - if (!view) NcError.notFound('Not found'); - if (view.type !== ViewTypes.GRID) NcError.notFound('Not found'); - - if (view.password && view.password !== req.headers?.['xc-password']) { - NcError.forbidden(ErrorMessages.INVALID_SHARED_VIEW_PASSWORD); - } - - const column = await getColumnByIdOrName( - req.params.colId, - await view.getModel() - ); - - if (column.fk_model_id !== view.fk_model_id) - NcError.badRequest("Column doesn't belongs to the model"); - - const base = await Base.get(view.base_id); - - const baseModel = await Model.getBaseModelSQL({ - id: view.fk_model_id, - viewId: view?.id, - dbDriver: NcConnectionMgrv2.get(base), - }); - - const key = `List`; - const requestObj: any = { - [key]: 1, - }; - - const data = ( - await nocoExecute( - requestObj, - { - [key]: async (args) => { - return await baseModel.hmList( - { - colId: req.params.colId, - id: req.params.rowId, - }, - args - ); - }, - }, - {}, - { nested: { [key]: req.query } } - ) - )?.[key]; - - const count = await baseModel.hmListCount({ - colId: req.params.colId, - id: req.params.rowId, + const paginatedResponse = await publicDataService.publicHmList({ + query: req.query, + password: req.headers?.['xc-password'] as string, + sharedViewUuid: req.params.sharedViewUuid, + columnId: req.params.columnId, + rowId: req.params.rowId, }); - - res.json(new PagedResponseImpl(data, { ...req.query, count })); + res.json(paginatedResponse); } const router = Router({ mergeParams: true }); diff --git a/packages/nocodb/src/lib/controllers/publicApis/publicMetaApis.ts b/packages/nocodb/src/lib/controllers/publicApis/publicMetaApis.ts index 7d5af6036f..238e3223ae 100644 --- a/packages/nocodb/src/lib/controllers/publicApis/publicMetaApis.ts +++ b/packages/nocodb/src/lib/controllers/publicApis/publicMetaApis.ts @@ -1,96 +1,21 @@ import { Request, Response, Router } from 'express'; -import catchError, { NcError } from '../../meta/helpers/catchError'; -import View from '../../models/View'; -import Model from '../../models/Model'; -import { - ErrorMessages, - LinkToAnotherRecordType, - RelationTypes, - UITypes, -} from 'nocodb-sdk'; -import Column from '../../models/Column'; -import Base from '../../models/Base'; -import Project from '../../models/Project'; -import LinkToAnotherRecordColumn from '../../models/LinkToAnotherRecordColumn'; +import catchError from '../../meta/helpers/catchError'; +import { publicMetaService } from '../../services'; export async function viewMetaGet(req: Request, res: Response) { - const view: View & { - relatedMetas?: { [ket: string]: Model }; - client?: string; - } = await View.getByUUID(req.params.sharedViewUuid); - - if (!view) NcError.notFound('Not found'); - - if (view.password && view.password !== req.headers?.['xc-password']) { - NcError.forbidden(ErrorMessages.INVALID_SHARED_VIEW_PASSWORD); - } - - await view.getFilters(); - await view.getSorts(); - - await view.getViewWithInfo(); - await view.getColumns(); - await view.getModelWithInfo(); - await view.model.getColumns(); - - const base = await Base.get(view.model.base_id); - view.client = base.type; - - // todo: return only required props - delete view['password']; - - view.model.columns = view.columns - .filter((c) => { - const column = view.model.columnsById[c.fk_column_id]; - return ( - c.show || - (column.rqd && !column.cdf && !column.ai) || - column.pk || - view.model.columns.some( - (c1) => - c1.uidt === UITypes.LinkToAnotherRecord && - (c1.colOptions).type === - RelationTypes.BELONGS_TO && - view.columns.some((vc) => vc.fk_column_id === c1.id && vc.show) && - (c1.colOptions).fk_child_column_id === - c.fk_column_id - ) - ); + res.json( + await publicMetaService.viewMetaGet({ + password: req.headers?.['xc-password'] as string, + sharedViewUuid: req.params.sharedViewUuid, }) - .map( - (c) => - new Column({ ...c, ...view.model.columnsById[c.fk_column_id] } as any) - ) as any; - - const relatedMetas = {}; - - // load related table metas - for (const col of view.model.columns) { - if (UITypes.LinkToAnotherRecord === col.uidt) { - const colOpt = await col.getColOptions(); - relatedMetas[colOpt.fk_related_model_id] = await Model.getWithInfo({ - id: colOpt.fk_related_model_id, - }); - if (colOpt.type === 'mm') { - relatedMetas[colOpt.fk_mm_model_id] = await Model.getWithInfo({ - id: colOpt.fk_mm_model_id, - }); - } - } - } - - view.relatedMetas = relatedMetas; - - res.json(view); + ); } async function publicSharedBaseGet(req, res): Promise { - const project = await Project.getByUuid(req.params.sharedBaseUuid); - - if (!project) { - NcError.notFound(); - } - - res.json({ project_id: project.id }); + res.json( + await publicMetaService.publicSharedBaseGet({ + sharedBaseUuid: req.params.sharedBaseUuid, + }) + ); } const router = Router({ mergeParams: true });