Browse Source

refcator: handler to service-controller - project, column, view, hook, base (WIP)

Signed-off-by: Pranav C <pranavxc@gmail.com>
pull/5239/head
Pranav C 2 years ago
parent
commit
89adb8dafa
  1. 130
      packages/nocodb/src/lib/controllers/filterController.ts
  2. 177
      packages/nocodb/src/lib/controllers/projectController.ts
  3. 50
      packages/nocodb/src/lib/controllers/sortController.ts
  4. 83
      packages/nocodb/src/lib/controllers/viewController.ts
  5. 8
      packages/nocodb/src/lib/models/Hook.ts
  6. 2
      packages/nocodb/src/lib/models/Project.ts
  7. 3
      packages/nocodb/src/lib/models/View.ts
  8. 36
      packages/nocodb/src/lib/models/index.ts
  9. 30
      packages/nocodb/src/lib/services/apiTokenService.ts
  10. 78
      packages/nocodb/src/lib/services/baseService.ts
  11. 57
      packages/nocodb/src/lib/services/filterService.ts
  12. 123
      packages/nocodb/src/lib/services/hookService.ts
  13. 6
      packages/nocodb/src/lib/services/index.ts
  14. 211
      packages/nocodb/src/lib/services/projectService.ts
  15. 33
      packages/nocodb/src/lib/services/sortService.ts
  16. 85
      packages/nocodb/src/lib/services/viewService.ts

130
packages/nocodb/src/lib/controllers/filterController.ts

@ -1,119 +1,73 @@
import { Request, Response, Router } from 'express';
// @ts-ignore
import Model from '../models/Model';
import { Tele } from 'nc-help';
// @ts-ignore
import { PagedResponseImpl } from '../meta/helpers/PagedResponse';
// @ts-ignore
import { Table, TableList, TableListParams, TableReq } from 'nocodb-sdk';
// @ts-ignore
import ProjectMgrv2 from '../db/sql-mgr/v2/ProjectMgrv2';
// @ts-ignore
import Project from '../models/Project';
import Filter from '../models/Filter';
import { FilterReqType } from 'nocodb-sdk';
import { getAjvValidatorMw } from '../meta/api/helpers';
import ncMetaAclMw from '../meta/helpers/ncMetaAclMw';
import { metaApiMetrics } from '../meta/helpers/apiMetrics';
import { getAjvValidatorMw } from '../meta/api/helpers';
import { filterService } from '../services';
// @ts-ignore
export async function filterGet(req: Request, res: Response) {
res.json(await Filter.get(req.params.filterId));
res.json(await filterService.filterGet({ filterId: req.params.filterId }));
}
// @ts-ignore
export async function filterList(
req: Request<any, any, any, TableListParams>,
res: Response,
next
) {
export async function filterList(req: Request, res: Response) {
res.json(
await Filter.rootFilterList({
await filterService.filterList({
viewId: req.params.viewId,
})
);
}
// @ts-ignore
export async function filterChildrenRead(
req: Request<any, any, any, TableListParams>,
res: Response,
next
) {
try {
const filter = await Filter.parentFilterList({
parentId: req.params.filterParentId,
});
export async function filterChildrenRead(req: Request, res: Response) {
const filter = await filterService.filterChildrenList({
filterId: req.params.filterParentId,
});
res.json(filter);
} catch (e) {
console.log(e);
next(e);
}
res.json(filter);
}
export async function filterCreate(
req: Request<any, any, TableReq>,
res,
next
) {
try {
const filter = await Filter.insert({
...req.body,
fk_view_id: req.params.viewId,
});
export async function filterCreate(req: Request<any, any, FilterReqType>, res) {
const filter = await filterService.filterCreate({
filter: req.body,
viewId: req.params.viewId,
});
res.json(filter);
}
Tele.emit('evt', { evt_type: 'filter:created' });
res.json(filter);
} catch (e) {
console.log(e);
next(e);
}
export async function filterUpdate(req, res) {
const filter = await filterService.filterUpdate({
filterId: req.params.filterId,
filter: req.body,
});
res.json(filter);
}
// @ts-ignore
export async function filterUpdate(req, res, next) {
try {
const filter = await Filter.update(req.params.filterId, {
...req.body,
fk_view_id: req.params.viewId,
});
Tele.emit('evt', { evt_type: 'filter:updated' });
res.json(filter);
} catch (e) {
console.log(e);
next(e);
}
export async function filterDelete(req: Request, res: Response) {
const filter = await filterService.filterDelete({
filterId: req.params.filterId,
});
res.json(filter);
}
// @ts-ignore
export async function filterDelete(req: Request, res: Response, next) {
try {
const filter = await Filter.delete(req.params.filterId);
Tele.emit('evt', { evt_type: 'filter:deleted' });
res.json(filter);
} catch (e) {
console.log(e);
next(e);
}
export async function hookFilterList(req: Request, res: Response) {
res.json(
await filterService.hookFilterList({
hookId: req.params.hookId,
})
);
}
export async function hookFilterList(
req: Request<any, any, any, TableListParams>,
res: Response
export async function hookFilterCreate(
req: Request<any, any, FilterReqType>,
res
) {
const filter = await Filter.rootFilterListByHook({
const filter = await filterService.hookFilterCreate({
filter: req.body,
hookId: req.params.hookId,
});
res.json(filter);
}
export async function hookFilterCreate(req: Request<any, any, TableReq>, res) {
const filter = await Filter.insert({
...req.body,
fk_hook_id: req.params.hookId,
});
Tele.emit('evt', { evt_type: 'hookFilter:created' });
res.json(filter);
}

177
packages/nocodb/src/lib/controllers/projectController.ts

@ -1,27 +1,20 @@
import { Request, Response } from 'express';
import { OrgUserRoles, ProjectType } from 'nocodb-sdk';
import { ProjectType } from 'nocodb-sdk';
import Project from '../models/Project';
import { ProjectListType } from 'nocodb-sdk';
import DOMPurify from 'isomorphic-dompurify';
import { packageVersion } from '../utils/packageVersion';
import { Tele } from 'nc-help';
import { PagedResponseImpl } from '../meta/helpers/PagedResponse';
import syncMigration from '../meta/helpers/syncMigration';
import NcConnectionMgrv2 from '../utils/common/NcConnectionMgrv2';
import ncMetaAclMw from '../meta/helpers/ncMetaAclMw';
import ProjectUser from '../models/ProjectUser';
import { customAlphabet } from 'nanoid';
import Noco from '../Noco';
import isDocker from 'is-docker';
import { NcError } from '../meta/helpers/catchError';
import { metaApiMetrics } from '../meta/helpers/apiMetrics';
import { extractPropsAndSanitize } from '../meta/helpers/extractProps';
import NcConfigFactory from '../utils/NcConfigFactory';
import { promisify } from 'util';
import { getAjvValidatorMw, populateMeta } from '../meta/api/helpers';
import { getAjvValidatorMw } from '../meta/api/helpers';
import Filter from '../models/Filter';
const nanoid = customAlphabet('1234567890abcdefghijklmnopqrstuvwxyz_', 4);
import { projectService } from '../services';
// // Project CRUD
@ -29,13 +22,12 @@ export async function projectGet(
req: Request<any, any, any>,
res: Response<Project>
) {
const project = await Project.getWithInfo(req.params.projectId);
// delete datasource connection details
project.bases?.forEach((b) => {
['config'].forEach((k) => delete b[k]);
const project = await projectService.getProjectWithInfo({
projectId: req.params.projectId,
});
projectService.sanitizeProject(project);
res.json(project);
}
@ -43,154 +35,49 @@ export async function projectUpdate(
req: Request<any, any, any>,
res: Response<ProjectListType>
) {
const project = await Project.getWithInfo(req.params.projectId);
const data: Partial<Project> = extractPropsAndSanitize(req?.body, [
'title',
'meta',
'color',
]);
if (
data?.title &&
project.title !== data.title &&
(await Project.getByTitle(data.title))
) {
NcError.badRequest('Project title already in use');
}
const project = await projectService.projectUpdate({
projectId: req.params.projectId,
project: req.body,
});
const result = await Project.update(req.params.projectId, data);
Tele.emit('evt', { evt_type: 'project:update' });
res.json(result);
res.json(project);
}
export async function projectList(
req: Request<any> & { user: { id: string; roles: string } },
res: Response<ProjectListType>,
next
res: Response<ProjectListType>
) {
try {
const projects = req.user?.roles?.includes(OrgUserRoles.SUPER_ADMIN)
? await Project.list(req.query)
: await ProjectUser.getProjectsList(req.user.id, req.query);
const projects = await projectService.projectList({
user: req.user,
query: req.query,
});
res // todo: pagination
.json(
new PagedResponseImpl(projects as ProjectType[], {
count: projects.length,
limit: projects.length,
})
);
} catch (e) {
console.log(e);
next(e);
}
res.json(
new PagedResponseImpl(projects as ProjectType[], {
count: projects.length,
limit: projects.length,
})
);
}
export async function projectDelete(
req: Request<any>,
res: Response<ProjectListType>
res: Response<boolean>
) {
const result = await Project.softDelete(req.params.projectId);
Tele.emit('evt', { evt_type: 'project:deleted' });
res.json(result);
}
//
//
async function projectCreate(req: Request<any, any>, res) {
const projectBody = req.body;
if (!projectBody.external) {
const ranId = nanoid();
projectBody.prefix = `nc_${ranId}__`;
projectBody.is_meta = true;
if (process.env.NC_MINIMAL_DBS) {
// if env variable NC_MINIMAL_DBS is set, then create a SQLite file/connection for each project
// each file will be named as nc_<random_id>.db
const fs = require('fs');
const toolDir = NcConfigFactory.getToolDir();
const nanoidv2 = customAlphabet(
'1234567890abcdefghijklmnopqrstuvwxyz',
14
);
if (!(await promisify(fs.exists)(`${toolDir}/nc_minimal_dbs`))) {
await promisify(fs.mkdir)(`${toolDir}/nc_minimal_dbs`);
}
const dbId = nanoidv2();
const projectTitle = DOMPurify.sanitize(projectBody.title);
projectBody.prefix = '';
projectBody.bases = [
{
type: 'sqlite3',
config: {
client: 'sqlite3',
connection: {
client: 'sqlite3',
database: projectTitle,
connection: {
filename: `${toolDir}/nc_minimal_dbs/${projectTitle}_${dbId}.db`,
},
},
},
inflection_column: 'camelize',
inflection_table: 'camelize',
},
];
} else {
const db = Noco.getConfig().meta?.db;
projectBody.bases = [
{
type: db?.client,
config: null,
is_meta: true,
inflection_column: 'camelize',
inflection_table: 'camelize',
},
];
}
} else {
if (process.env.NC_CONNECT_TO_EXTERNAL_DB_DISABLED) {
NcError.badRequest('Connecting to external db is disabled');
}
projectBody.is_meta = false;
}
if (projectBody?.title.length > 50) {
NcError.badRequest('Project title exceeds 50 characters');
}
if (await Project.getByTitle(projectBody?.title)) {
NcError.badRequest('Project title already in use');
}
projectBody.title = DOMPurify.sanitize(projectBody.title);
projectBody.slug = projectBody.title;
const project = await Project.createProject(projectBody);
await ProjectUser.insert({
fk_user_id: (req as any).user.id,
project_id: project.id,
roles: 'owner',
const deleted = await projectService.projectSoftDelete({
projectId: req.params.projectId,
});
await syncMigration(project);
// populate metadata if existing table
for (const base of await project.getBases()) {
const info = await populateMeta(base, project);
res.json(deleted);
}
Tele.emit('evt_api_created', info);
delete base.config;
}
Tele.emit('evt', {
evt_type: 'project:created',
xcdb: !projectBody.external,
async function projectCreate(req: Request<any, any>, res) {
const project = await projectService.projectCreate({
project: req.body,
user: req['user'],
});
Tele.emit('evt', { evt_type: 'project:rest' });
res.json(project);
}

50
packages/nocodb/src/lib/controllers/sortController.ts

@ -1,52 +1,52 @@
import { Request, Response, Router } from 'express';
// @ts-ignore
import Model from '../models/Model';
import { Tele } from 'nc-help';
// @ts-ignore
import { getAjvValidatorMw } from '../meta/api/helpers'
import { PagedResponseImpl } from '../meta/helpers/PagedResponse';
import { SortListType, SortType, TableType } from 'nocodb-sdk';
// @ts-ignore
import ProjectMgrv2 from '../db/sql-mgr/v2/ProjectMgrv2';
// @ts-ignore
import Project from '../models/Project';
import Sort from '../models/Sort';
import { SortListType, SortReqType } from 'nocodb-sdk';
import ncMetaAclMw from '../meta/helpers/ncMetaAclMw';
import { metaApiMetrics } from '../meta/helpers/apiMetrics';
import { getAjvValidatorMw } from '../meta/api/helpers';
// @ts-ignore
export async function sortGet(req: Request, res: Response<TableType>) {}
import { sortService } from '../services';
// @ts-ignore
export async function sortList(
req: Request<any, any, any>,
res: Response<SortListType>
) {
const sortList = await Sort.list({ viewId: req.params.viewId });
const sortList = await sortService.sortList({
viewId: req.params.viewId,
});
res.json({
sorts: new PagedResponseImpl(sortList),
});
}
// @ts-ignore
export async function sortCreate(req: Request<any, any, SortType>, res) {
const sort = await Sort.insert({
...req.body,
fk_view_id: req.params.viewId,
} as Sort);
Tele.emit('evt', { evt_type: 'sort:created' });
export async function sortCreate(req: Request<any, any, SortReqType>, res) {
const sort = await sortService.sortCreate({
sort: req.body,
viewId: req.params.viewId,
});
res.json(sort);
}
export async function sortUpdate(req, res) {
const sort = await Sort.update(req.params.sortId, req.body);
Tele.emit('evt', { evt_type: 'sort:updated' });
const sort = await sortService.sortUpdate({
sortId: req.params.sortId,
sort: req.body,
});
res.json(sort);
}
export async function sortDelete(req: Request, res: Response) {
Tele.emit('evt', { evt_type: 'sort:deleted' });
const sort = await Sort.delete(req.params.sortId);
const sort = await sortService.sortDelete({
sortId: req.params.sortId,
});
res.json(sort);
}
export async function sortGet(req: Request, res: Response) {
const sort = await sortService.sortGet({
sortId: req.params.sortId,
});
res.json(sort);
}
@ -62,11 +62,13 @@ router.post(
getAjvValidatorMw('swagger.json#/components/schemas/SortReq'),
ncMetaAclMw(sortCreate, 'sortCreate')
);
router.get(
'/api/v1/db/meta/sorts/:sortId',
metaApiMetrics,
ncMetaAclMw(sortGet, 'sortGet')
);
router.patch(
'/api/v1/db/meta/sorts/:sortId',
metaApiMetrics,

83
packages/nocodb/src/lib/controllers/viewController.ts

@ -1,43 +1,21 @@
import { Request, Response, Router } from 'express';
// @ts-ignore
import Model from '../models/Model';
import { Tele } from 'nc-help';
// @ts-ignore
import { PagedResponseImpl } from '../meta/helpers/PagedResponse';
// @ts-ignore
import { Table, TableReq, ViewList } from 'nocodb-sdk';
// @ts-ignore
import ProjectMgrv2 from '../db/sql-mgr/v2/ProjectMgrv2';
// @ts-ignore
import Project from '../models/Project';
import View from '../models/View';
import { View } from '../models';
import ncMetaAclMw from '../meta/helpers/ncMetaAclMw';
import { xcVisibilityMetaGet } from './modelVisibilityController';
import { metaApiMetrics } from '../meta/helpers/apiMetrics';
import { viewService } from '../services';
// @ts-ignore
export async function viewGet(req: Request, res: Response<Table>) {}
export async function viewGet(req: Request, res: Response) {}
// @ts-ignore
export async function viewList(
req: Request<any, any, any>,
res: Response<ViewList>
res: Response
) {
const model = await Model.get(req.params.tableId);
const viewList = await xcVisibilityMetaGet(
// req.params.projectId,
// req.params.baseId,
model.project_id,
[model]
);
//await View.list(req.params.tableId)
const filteredViewList = viewList.filter((view: any) => {
return Object.keys((req as any).session?.passport?.user?.roles).some(
(role) =>
(req as any)?.session?.passport?.user?.roles[role] &&
!view.disabled[role]
);
const filteredViewList = await viewService.viewList({
tableId: req.params.tableId,
user: (req as any).session?.passport?.user,
});
res.json(new PagedResponseImpl(filteredViewList));
@ -48,8 +26,7 @@ export async function shareView(
req: Request<any, any, any>,
res: Response<View>
) {
Tele.emit('evt', { evt_type: 'sharedView:generated-link' });
res.json(await View.share(req.params.viewId));
res.json(await viewService.shareView({ viewId: req.params.viewId }));
}
// @ts-ignore
@ -57,48 +34,56 @@ export async function viewCreate(req: Request<any, any>, res, next) {}
// @ts-ignore
export async function viewUpdate(req, res) {
const result = await View.update(req.params.viewId, req.body);
Tele.emit('evt', { evt_type: 'vtable:updated', show_as: result.type });
const result = await viewService.viewUpdate({
viewId: req.params.viewId,
view: req.body,
});
res.json(result);
}
// @ts-ignore
export async function viewDelete(req: Request, res: Response, next) {
const result = await View.delete(req.params.viewId);
Tele.emit('evt', { evt_type: 'vtable:deleted' });
const result = await viewService.viewDelete({ viewId: req.params.viewId });
res.json(result);
}
async function shareViewUpdate(req: Request<any, any>, res) {
Tele.emit('evt', { evt_type: 'sharedView:updated' });
res.json(await View.update(req.params.viewId, req.body));
res.json(
await viewService.shareViewUpdate({
viewId: req.params.viewId,
sharedView: req.body,
})
);
}
async function shareViewDelete(req: Request<any, any>, res) {
Tele.emit('evt', { evt_type: 'sharedView:deleted' });
res.json(await View.sharedViewDelete(req.params.viewId));
res.json(await viewService.shareViewDelete({ viewId: req.params.viewId }));
}
async function showAllColumns(req: Request<any, any>, res) {
res.json(
await View.showAllColumns(
req.params.viewId,
<string[]>(req.query?.ignoreIds || [])
)
await viewService.showAllColumns({
viewId: req.params.viewId,
ignoreIds: <string[]>(req.query?.ignoreIds || []),
})
);
}
async function hideAllColumns(req: Request<any, any>, res) {
res.json(
await View.hideAllColumns(
req.params.viewId,
<string[]>(req.query?.ignoreIds || [])
)
await viewService.hideAllColumns({
viewId: req.params.viewId,
ignoreIds: <string[]>(req.query?.ignoreIds || []),
})
);
}
async function shareViewList(req: Request<any, any>, res) {
res.json(await View.shareViewList(req.params.tableId));
res.json(
await viewService.shareViewList({
tableId: req.params.tableId,
})
);
}
const router = Router({ mergeParams: true });

8
packages/nocodb/src/lib/models/Hook.ts

@ -1,4 +1,4 @@
import { HookType } from 'nocodb-sdk';
import { BoolType, HookType } from 'nocodb-sdk'
import {
CacheDelDirection,
CacheGetType,
@ -21,16 +21,16 @@ export default class Hook implements HookType {
type?: string;
event?: 'after' | 'before';
operation?: 'insert' | 'delete' | 'update';
async?: boolean;
async?: BoolType;
payload?: string;
url?: string;
headers?: string;
condition?: boolean;
condition?: BoolType;
notification?: string;
retries?: number;
retry_interval?: number;
timeout?: number;
active?: boolean;
active?: BoolType;
project_id?: string;
base_id?: string;

2
packages/nocodb/src/lib/models/Project.ts

@ -1,6 +1,6 @@
import Base from './/Base';
import Noco from '../Noco';
import { BoolType, MetaType, ProjectType } from 'nocodb-sdk';
import { BoolType, MetaType, ProjectType } from 'nocodb-sdk'
import {
CacheDelDirection,
CacheGetType,

3
packages/nocodb/src/lib/models/View.ts

@ -15,6 +15,7 @@ import GridViewColumn from './GridViewColumn';
import Sort from './Sort';
import Filter from './Filter';
import {
BoolType,
ColumnReqType,
isSystemColumn,
UITypes,
@ -942,7 +943,7 @@ export default class View implements ViewType {
body: {
title?: string;
order?: number;
show_system_fields?: boolean;
show_system_fields?: BoolType;
lock_type?: string;
password?: string;
uuid?: string;

36
packages/nocodb/src/lib/models/index.ts

@ -0,0 +1,36 @@
export { default as ApiToken } from './ApiToken';
export { default as Audit } from './Audit';
export { default as BarcodeColumn } from './BarcodeColumn';
export { default as Base } from './Base';
export { default as Column } from './Column';
export { default as Filter } from './Filter';
export { default as FormulaColumn } from './FormulaColumn';
export { default as FormView } from './FormView';
export { default as FormViewColumn } from './FormViewColumn';
export { default as GalleryView } from './GalleryView';
export { default as GalleryViewColumn } from './GalleryViewColumn';
export { default as GridView } from './GridView';
export { default as GridViewColumn } from './GridViewColumn';
export { default as Hook } from './Hook';
export { default as HookFilter } from './HookFilter';
export { default as HookLog } from './HookLog';
export { default as KanbanView } from './KanbanView';
export { default as KanbanViewColumn } from './KanbanViewColumn';
export { default as LinkToAnotherRecordColumn } from './LinkToAnotherRecordColumn';
export { default as LookupColumn } from './LookupColumn';
export { default as MapView } from './MapView';
export { default as MapViewColumn } from './MapViewColumn';
export { default as Model } from './Model';
export { default as ModelRoleVisibility } from './ModelRoleVisibility';
export { default as Plugin } from './Plugin';
export { default as Project } from './Project';
export { default as ProjectUser } from './ProjectUser';
export { default as QrCodeColumn } from './QrCodeColumn';
export { default as RollupColumn } from './RollupColumn';
export { default as SelectOption } from './SelectOption';
export { default as Sort } from './Sort';
export { default as Store } from './Store';
export { default as SyncLogs } from './SyncLogs';
export { default as SyncSource } from './SyncSource';
export { default as User } from './User';
export { default as View } from './View';

30
packages/nocodb/src/lib/services/apiTokenService.ts

@ -0,0 +1,30 @@
import { ApiTokenReqType, OrgUserRoles } from 'nocodb-sdk';
import { Tele } from 'nc-help';
import { NcError } from '../meta/helpers/catchError';
import ApiToken from '../models/ApiToken';
import User from '../models/User';
export async function apiTokenList(param: { userId: string }) {
return ApiToken.list(param.userId);
}
export async function apiTokenCreate(param: {
userId: string;
tokenBody: ApiTokenReqType;
}) {
Tele.emit('evt', { evt_type: 'apiToken:created' });
return ApiToken.insert({ ...param.tokenBody, fk_user_id: param.userId });
}
export async function apiTokenDelete(param: { token; user: User }) {
const apiToken = await ApiToken.getByToken(param.token);
if (
!param.user.roles.includes(OrgUserRoles.SUPER_ADMIN) &&
apiToken.fk_user_id !== param.user.id
) {
NcError.notFound('Token not found');
}
Tele.emit('evt', { evt_type: 'apiToken:deleted' });
// todo: verify token belongs to the user
return await ApiToken.delete(param.token);
}

78
packages/nocodb/src/lib/services/baseService.ts

@ -0,0 +1,78 @@
import Project from '../models/Project';
import { BaseReqType } from 'nocodb-sdk';
import { syncBaseMigration } from '../meta/helpers/syncMigration';
import Base from '../models/Base';
import { Tele } from 'nc-help';
import { populateMeta } from '../meta/api/helpers';
export async function baseGetWithConfig(param: { baseId: any }) {
const base = await Base.get(param.baseId);
base.config = base.getConnectionConfig();
return base;
}
export async function baseUpdate(param: {
baseId: string;
base: BaseReqType;
projectId: string;
}) {
const baseBody = param.base;
const project = await Project.getWithInfo(param.projectId);
const base = await Base.updateBase(param.baseId, {
...baseBody,
type: baseBody.config?.client,
projectId: project.id,
id: param.baseId,
});
delete base.config;
Tele.emit('evt', {
evt_type: 'base:updated',
});
return base;
}
export async function baseList(param: { projectId: string }) {
const bases = await Base.list({ projectId: param.projectId });
return bases;
}
export async function baseDelete(param: { baseId: string }) {
const base = await Base.get(param.baseId);
await base.delete();
Tele.emit('evt', { evt_type: 'base:deleted' });
return true;
}
export async function baseCreate(param: {
projectId: string;
base: BaseReqType;
}) {
// type | base | projectId
const baseBody = param.base;
const project = await Project.getWithInfo(param.projectId);
const base = await Base.createBase({
...baseBody,
type: baseBody.config?.client,
projectId: project.id,
});
await syncBaseMigration(project, base);
const info = await populateMeta(base, project);
Tele.emit('evt_api_created', info);
delete base.config;
Tele.emit('evt', {
evt_type: 'base:created',
});
return base;
}

57
packages/nocodb/src/lib/services/filterService.ts

@ -1,4 +1,61 @@
import { FilterReqType } from 'nocodb-sdk'
import Filter from '../models/Filter'
import { Tele } from 'nc-help';
export async function hookFilterCreate(param: { filter: FilterReqType; hookId: any }) {
const filter = await Filter.insert({
...param.filter,
fk_hook_id: param.hookId,
})
Tele.emit('evt', { evt_type: 'hookFilter:created' })
return filter
}
export async function hookFilterList(param: { hookId: any }) {
return Filter.rootFilterListByHook({ hookId: param.hookId })
}
export async function filterDelete(param: { filterId: string }) {
await Filter.delete(param.filterId)
Tele.emit('evt', { evt_type: 'filter:deleted' })
return true;
}
export async function filterCreate(param: {
filter: FilterReqType
viewId: string
}) {
const filter = await Filter.insert({
...param.filter,
fk_view_id: param.viewId,
});
Tele.emit('evt', { evt_type: 'filter:created' });
return filter
}
export async function filterUpdate(param: {
filter: FilterReqType
filterId: string
}) {
const filter = await Filter.update(param.filterId, param.filter)
Tele.emit('evt', { evt_type: 'filter:updated' })
return filter
}
export async function filterChildrenList(param: { filterId: any }) {
return Filter.parentFilterList({
parentId: param.filterId,
})
}
export async function filterGet(param: { filterId: string }) {
const filter = await Filter.get(param.filterId)

123
packages/nocodb/src/lib/services/hookService.ts

@ -0,0 +1,123 @@
import { Tele } from 'nc-help';
import catchError from '../helpers/catchError';
import { Request, Response, Router } from 'express';
import { Hook, Model } from '../models';
import { HookListType, HookReqType, HookTestReqType, HookType, TableReqType } from 'nocodb-sdk'
import { PagedResponseImpl } from '../helpers/PagedResponse';
import { invokeWebhook } from '../helpers/webhookHelpers';
import populateSamplePayload from '../helpers/populateSamplePayload';
import ncMetaAclMw from '../helpers/ncMetaAclMw';
import { metaApiMetrics } from '../helpers/apiMetrics';
import { getAjvValidatorMw } from './helpers';
export async function hookList(
param: {
tableId: string;
}
) {
// todo: pagination
await Hook.list({ fk_model_id: param.tableId }))
}
export async function hookCreate(
param: {
tableId: string;
hook: HookReqType
}
) {
Tele.emit('evt', { evt_type: 'webhooks:created' });
const hook = await Hook.insert({
...param.hook,
fk_model_id: param.tableId,
});
return hook;
}
export async function hookDelete(
param :{
hookId: string;
}
) {
Tele.emit('evt', { evt_type: 'webhooks:deleted' });
await Hook.delete(param.hookId);
return true;
}
export async function hookUpdate(
param: {
hookId: string;
hook: HookReqType;
}
) {
Tele.emit('evt', { evt_type: 'webhooks:updated' });
return await Hook.update(param.hookId, param.hook)
}
export async function hookTest(param: {
tableId: string;
hookTest: HookTestReqType;
}) {
const model = await Model.getByIdOrName({ id: param.tableId });
const {
hook,
payload: { data, user },
} = param.hookTest;
await invokeWebhook(
new Hook(hook),
model,
data,
user,
(hook as any)?.filters,
true
);
Tele.emit('evt', { evt_type: 'webhooks:tested' });
return true
}
export async function tableSampleData(param:{
tableId: string;
operation: 'insert' | 'update';
}) {
const model = await Model.getByIdOrName({ id: param.tableId });
return await populateSamplePayload(model, false, param.operation);
}
const router = Router({ mergeParams: true });
router.get(
'/api/v1/db/meta/tables/:tableId/hooks',
metaApiMetrics,
ncMetaAclMw(hookList, 'hookList')
);
router.post(
'/api/v1/db/meta/tables/:tableId/hooks/test',
metaApiMetrics,
getAjvValidatorMw('swagger.json#/components/schemas/HookTestReq'),
ncMetaAclMw(hookTest, 'hookTest')
);
router.post(
'/api/v1/db/meta/tables/:tableId/hooks',
metaApiMetrics,
getAjvValidatorMw('swagger.json#/components/schemas/HookReq'),
ncMetaAclMw(hookCreate, 'hookCreate')
);
router.delete(
'/api/v1/db/meta/hooks/:hookId',
metaApiMetrics,
ncMetaAclMw(hookDelete, 'hookDelete')
);
router.patch(
'/api/v1/db/meta/hooks/:hookId',
metaApiMetrics,
getAjvValidatorMw('swagger.json#/components/schemas/HookReq'),
ncMetaAclMw(hookUpdate, 'hookUpdate')
);
router.get(
'/api/v1/db/meta/tables/:tableId/hooks/samplePayload/:operation',
metaApiMetrics,
catchError(tableSampleData)
);
export default router;

6
packages/nocodb/src/lib/services/index.ts

@ -1,4 +1,8 @@
// export * as projectService from './projectService';
export * as projectService from './projectService';
export * as tableService from './tableService';
export * as columnService from './columnService';
export * as filterService from './filterService';
export * as sortService from './sortService';
export * as baseService from './baseService';
export * as apiTokenService from './apiTokenService';
export * as viewService from './viewService';

211
packages/nocodb/src/lib/services/projectService.ts

@ -1,42 +1,169 @@
// // services/project.service.ts
// import { OrgUserRoles, ProjectType } from 'nocodb-sdk'
// import Project from '../models/Project'
// import ProjectUser from '../models/ProjectUser'
// import { customAlphabet } from 'nanoid'
// import { NcError } from './helpers/catchError'
//
// const nanoid = customAlphabet('1234567890abcdefghijklmnopqrstuvwxyz_', 4)
//
// export async function getProjectWithInfo(projectId: string) {
// const project = await Project.getWithInfo(projectId)
// return project
// }
//
// export function sanitizeProject(project: any) {
// const sanitizedProject = { ...project }
// sanitizedProject.bases?.forEach((b: any) => {
// ['config'].forEach((k) => delete b[k])
// })
// return sanitizedProject
// }
//
// export async function updateProject(projectId: string, data: Partial<ProjectType>) {
// const project = await Project.getWithInfo(projectId)
// if (data?.title && project.title !== data.title && (await Project.getByTitle(data.title))) {
// NcError.badRequest('Project title already in use')
// }
// const result = await Project.update(projectId, data)
// return result
// }
//
// export async function listProjects(user: any) {
// const projects = user?.roles?.includes(OrgUserRoles.SUPER_ADMIN)
// ? await Project.list()
// : await ProjectUser.getProjectsList(user.id)
// return projects as ProjectType[]
// }
//
// export async function softDeleteProject(projectId: string) {
// const result = await Project.softDelete(projectId)
// return result
// }
import DOMPurify from 'isomorphic-dompurify';
import { OrgUserRoles, ProjectReqType, } from 'nocodb-sdk';
import { promisify } from 'util';
import { populateMeta } from '../meta/api/helpers';
import { extractPropsAndSanitize } from '../meta/helpers/extractProps';
import syncMigration from '../meta/helpers/syncMigration';
import Project from '../models/Project';
import ProjectUser from '../models/ProjectUser';
import { customAlphabet } from 'nanoid';
import Noco from '../Noco';
import NcConfigFactory from '../utils/NcConfigFactory';
import { NcError } from '../meta/helpers/catchError';
export async function projectCreate(param: {
project: ProjectReqType;
user: any;
}) {
const projectBody: ProjectReqType & Record<string, any> = param.project;
if (!projectBody.external) {
const ranId = nanoid();
projectBody.prefix = `nc_${ranId}__`;
projectBody.is_meta = true;
if (process.env.NC_MINIMAL_DBS) {
// if env variable NC_MINIMAL_DBS is set, then create a SQLite file/connection for each project
// each file will be named as nc_<random_id>.db
const fs = require('fs');
const toolDir = NcConfigFactory.getToolDir();
const nanoidv2 = customAlphabet(
'1234567890abcdefghijklmnopqrstuvwxyz',
14
);
if (!(await promisify(fs.exists)(`${toolDir}/nc_minimal_dbs`))) {
await promisify(fs.mkdir)(`${toolDir}/nc_minimal_dbs`);
}
const dbId = nanoidv2();
const projectTitle = DOMPurify.sanitize(projectBody.title);
projectBody.prefix = '';
projectBody.bases = [
{
type: 'sqlite3',
config: {
client: 'sqlite3',
connection: {
client: 'sqlite3',
database: projectTitle,
connection: {
filename: `${toolDir}/nc_minimal_dbs/${projectTitle}_${dbId}.db`,
},
},
},
inflection_column: 'camelize',
inflection_table: 'camelize',
},
];
} else {
const db = Noco.getConfig().meta?.db;
projectBody.bases = [
{
type: db?.client,
config: null,
is_meta: true,
inflection_column: 'camelize',
inflection_table: 'camelize',
},
];
}
} else {
if (process.env.NC_CONNECT_TO_EXTERNAL_DB_DISABLED) {
NcError.badRequest('Connecting to external db is disabled');
}
projectBody.is_meta = false;
}
if (projectBody?.title.length > 50) {
NcError.badRequest('Project title exceeds 50 characters');
}
if (await Project.getByTitle(projectBody?.title)) {
NcError.badRequest('Project title already in use');
}
projectBody.title = DOMPurify.sanitize(projectBody.title);
projectBody.slug = projectBody.title;
const project = await Project.createProject(projectBody);
await ProjectUser.insert({
fk_user_id: (param as any).user.id,
project_id: project.id,
roles: 'owner',
});
await syncMigration(project);
// populate metadata if existing table
for (const base of await project.getBases()) {
const info = await populateMeta(base, project);
Tele.emit('evt_api_created', info);
delete base.config;
}
Tele.emit('evt', {
evt_type: 'project:created',
xcdb: !projectBody.external,
});
Tele.emit('evt', { evt_type: 'project:rest' });
project;
}
const nanoid = customAlphabet('1234567890abcdefghijklmnopqrstuvwxyz_', 4);
import { Tele } from 'nc-help';
export async function getProjectWithInfo(param: { projectId: string }) {
const project = await Project.getWithInfo(param.projectId);
return project;
}
export async function projectSoftDelete(param: { projectId: any }) {
await Project.softDelete(param.projectId);
Tele.emit('evt', { evt_type: 'project:deleted' });
return true;
}
export function sanitizeProject(project: any) {
const sanitizedProject = { ...project };
sanitizedProject.bases?.forEach((b: any) => {
['config'].forEach((k) => delete b[k]);
});
return sanitizedProject;
}
export async function projectUpdate(param: {
projectId: string;
project: ProjectReqType;
}) {
const project = await Project.getWithInfo(param.projectId);
const data: Partial<Project> = extractPropsAndSanitize(
param?.project as Project,
['title', 'meta', 'color']
);
if (
data?.title &&
project.title !== data.title &&
(await Project.getByTitle(data.title))
) {
NcError.badRequest('Project title already in use');
}
const result = await Project.update(param.projectId, data);
Tele.emit('evt', { evt_type: 'project:update' });
return result;
}
export async function projectList(param: {
user: { id: string; roles: string };
query?: any;
}) {
const projects = param.user?.roles?.includes(OrgUserRoles.SUPER_ADMIN)
? await Project.list(param.query)
: await ProjectUser.getProjectsList(param.user.id, param.query);
return projects;
}

33
packages/nocodb/src/lib/services/sortService.ts

@ -0,0 +1,33 @@
import { SortReqType } from 'nocodb-sdk';
import Sort from '../models/Sort';
import { Tele } from 'nc-help';
export async function sortGet(param: { sortId: string }) {
return Sort.get(param.sortId);
}
export async function sortDelete(param: { sortId: string }) {
await Sort.delete(param.sortId);
Tele.emit('evt', { evt_type: 'sort:deleted' });
return true;
}
export async function sortUpdate(param: { sortId: any; sort: SortReqType }) {
const sort = await Sort.update(param.sortId, param.sort);
Tele.emit('evt', { evt_type: 'sort:updated' });
return sort;
}
export async function sortCreate(param: { viewId: any; sort: SortReqType }) {
const sort = await Sort.insert({
...param.sort,
fk_view_id: param.viewId,
} as Sort);
Tele.emit('evt', { evt_type: 'sort:created' });
return sort;
}
export async function sortList(param: { viewId: string }) {
return Sort.list({ viewId: param.viewId });
}

85
packages/nocodb/src/lib/services/viewService.ts

@ -0,0 +1,85 @@
import { Model, View } from '../models';
import { Tele } from 'nc-help';
import { SharedViewReqType, ViewReqType } from 'nocodb-sdk';
import { xcVisibilityMetaGet } from '../meta/api/modelVisibilityApis';
export async function viewList(param: {
tableId: string;
user: {
roles: Record<string, boolean>;
};
}) {
const model = await Model.get(param.tableId);
const viewList = await xcVisibilityMetaGet(
// param.projectId,
// param.baseId,
model.project_id,
[model]
);
// todo: user roles
//await View.list(param.tableId)
const filteredViewList = viewList.filter((view: any) => {
return Object.keys(param?.user?.roles).some(
(role) => param?.user?.roles[role] && !view.disabled[role]
);
});
return filteredViewList;
}
// @ts-ignore
export async function shareView(param: { viewId: string }) {
Tele.emit('evt', { evt_type: 'sharedView:generated-link' });
return await View.share(param.viewId);
}
// @ts-ignore
export async function viewUpdate(param: { viewId: string; view: ViewReqType }) {
const result = await View.update(param.viewId, param.view);
Tele.emit('evt', { evt_type: 'vtable:updated', show_as: result.type });
return result;
}
// @ts-ignore
export async function viewDelete(param: { viewId: string }) {
await View.delete(param.viewId);
Tele.emit('evt', { evt_type: 'vtable:deleted' });
return true;
}
export async function shareViewUpdate(param: {
viewId: string;
sharedView: SharedViewReqType;
}) {
Tele.emit('evt', { evt_type: 'sharedView:updated' });
return await View.update(param.viewId, param.sharedView);
}
export async function shareViewDelete(param: { viewId: string }) {
Tele.emit('evt', { evt_type: 'sharedView:deleted' });
await View.sharedViewDelete(param.viewId);
return true;
}
export async function showAllColumns(param: {
viewId: string;
ignoreIds?: string[];
}) {
await View.showAllColumns(param.viewId, param.ignoreIds || []);
return true;
}
export async function hideAllColumns(param: {
viewId: string;
ignoreIds?: string[];
}) {
await View.hideAllColumns(param.viewId, param.ignoreIds || []);
return true;
}
export async function shareViewList(param: { tableId: string }) {
return await View.shareViewList(param.tableId);
}
Loading…
Cancel
Save