Browse Source

refactor: use service in public api handlers

Signed-off-by: Pranav C <pranavxc@gmail.com>
pull/5239/head
Pranav C 2 years ago
parent
commit
7a5badcab5
  1. 12
      packages/nocodb/src/lib/controllers/publicApis/index.ts
  2. 443
      packages/nocodb/src/lib/controllers/publicApis/publicDataApis.ts
  3. 99
      packages/nocodb/src/lib/controllers/publicApis/publicMetaApis.ts

12
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,
};

443
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({
const pagedResponse = await publicDataService.dataList({
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 }),
password: req.headers?.['xc-password'] as string,
sharedViewUuid: req.params.sharedViewUuid,
});
} catch (e) {
console.log(e);
res.status(500).json({ msg: e.message });
}
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 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,
const groupedData = await publicDataService.groupedDataList({
query: req.query,
password: req.headers?.['xc-password'] as string,
sharedViewUuid: req.params.sharedViewUuid,
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<LinkToAnotherRecordColumn>();
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 });

99
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 &&
(<LinkToAnotherRecordColumn>c1.colOptions).type ===
RelationTypes.BELONGS_TO &&
view.columns.some((vc) => vc.fk_column_id === c1.id && vc.show) &&
(<LinkToAnotherRecordColumn>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<LinkToAnotherRecordType>();
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<any> {
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 });

Loading…
Cancel
Save