mirror of https://github.com/nocodb/nocodb
Pranav C
2 years ago
3 changed files with 619 additions and 3 deletions
@ -1,7 +1,131 @@ |
|||||||
import { Controller } from '@nestjs/common'; |
import { |
||||||
|
Controller, |
||||||
|
Get, |
||||||
|
Param, |
||||||
|
Query, |
||||||
|
UseGuards, |
||||||
|
Request, |
||||||
|
Post, |
||||||
|
Body, |
||||||
|
Patch, |
||||||
|
Delete, |
||||||
|
} from '@nestjs/common'; |
||||||
|
import { AuthGuard } from '@nestjs/passport'; |
||||||
|
import { TableReqType } from 'nocodb-sdk'; |
||||||
|
import extractRolesObj from 'src/utils/extractRolesObj'; |
||||||
|
import { PagedResponseImpl } from '../../helpers/PagedResponse'; |
||||||
|
import { |
||||||
|
ExtractProjectIdMiddleware, |
||||||
|
UseAclMiddleware, |
||||||
|
} from '../../middlewares/extract-project-id/extract-project-id.middleware'; |
||||||
import { TablesService } from './tables.service'; |
import { TablesService } from './tables.service'; |
||||||
|
|
||||||
@Controller('tables') |
@Controller() |
||||||
|
@UseGuards(ExtractProjectIdMiddleware, AuthGuard('jwt')) |
||||||
export class TablesController { |
export class TablesController { |
||||||
constructor(private readonly tablesService: TablesService) {} |
constructor(private readonly tablesService: TablesService) {} |
||||||
|
|
||||||
|
@Get([ |
||||||
|
'/api/v1/db/meta/projects/:projectId/tables', |
||||||
|
'/api/v1/db/meta/projects/:projectId/:baseId/tables', |
||||||
|
]) |
||||||
|
@UseAclMiddleware({ |
||||||
|
permissionName: 'tableList', |
||||||
|
}) |
||||||
|
async tableList( |
||||||
|
@Param('projectId') projectId: string, |
||||||
|
@Param('baseId') baseId: string, |
||||||
|
@Query('includeM2M') includeM2M: string, |
||||||
|
@Request() req, |
||||||
|
) { |
||||||
|
return new PagedResponseImpl( |
||||||
|
await this.tablesService.getAccessibleTables({ |
||||||
|
projectId, |
||||||
|
baseId, |
||||||
|
includeM2M: includeM2M === 'true', |
||||||
|
roles: extractRolesObj(req.user.roles), |
||||||
|
}), |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
@Post([ |
||||||
|
'/api/v1/db/meta/projects/:projectId/tables', |
||||||
|
'/api/v1/db/meta/projects/:projectId/:baseId/tables', |
||||||
|
]) |
||||||
|
@UseAclMiddleware({ |
||||||
|
permissionName: 'tableCreate', |
||||||
|
}) |
||||||
|
async tableCreate( |
||||||
|
@Param('projectId') projectId: string, |
||||||
|
@Param('baseId') baseId: string, |
||||||
|
@Body() body: TableReqType, |
||||||
|
@Request() req, |
||||||
|
) { |
||||||
|
const result = await this.tablesService.tableCreate({ |
||||||
|
projectId: projectId, |
||||||
|
baseId: baseId, |
||||||
|
table: body, |
||||||
|
user: req.user, |
||||||
|
}); |
||||||
|
|
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
@Get('/api/v1/db/meta/tables/:tableId') |
||||||
|
@UseAclMiddleware({ |
||||||
|
permissionName: 'tableGet', |
||||||
|
}) |
||||||
|
async tableGet(@Param('tableId') tableId: string, @Request() req) { |
||||||
|
const table = await this.tablesService.getTableWithAccessibleViews({ |
||||||
|
tableId: req.params.tableId, |
||||||
|
user: req.user, |
||||||
|
}); |
||||||
|
|
||||||
|
return table; |
||||||
|
} |
||||||
|
|
||||||
|
@Patch('/api/v1/db/meta/tables/:tableId') |
||||||
|
@UseAclMiddleware({ |
||||||
|
permissionName: 'tableUpdate', |
||||||
|
}) |
||||||
|
async tableUpdate( |
||||||
|
@Param('tableId') tableId: string, |
||||||
|
@Body() body: TableReqType, |
||||||
|
@Request() req, |
||||||
|
) { |
||||||
|
await this.tablesService.tableUpdate({ |
||||||
|
tableId: tableId, |
||||||
|
table: body, |
||||||
|
projectId: req.user, |
||||||
|
}); |
||||||
|
return { msg: 'The table has been updated successfully' }; |
||||||
|
} |
||||||
|
|
||||||
|
@Delete('/api/v1/db/meta/tables/:tableId') |
||||||
|
@UseAclMiddleware({ |
||||||
|
permissionName: 'tableDelete', |
||||||
|
}) |
||||||
|
async tableDelete(@Param('tableId') tableId: string, @Request() req) { |
||||||
|
const result = await this.tablesService.tableDelete({ |
||||||
|
tableId: req.params.tableId, |
||||||
|
user: (req as any).user, |
||||||
|
req, |
||||||
|
}); |
||||||
|
|
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
@Post('/api/v1/db/meta/tables/:tableId/reorder') |
||||||
|
@UseAclMiddleware({ |
||||||
|
permissionName: 'tableReorder', |
||||||
|
}) |
||||||
|
async tableReorder( |
||||||
|
@Param('tableId') tableId: string, |
||||||
|
@Body() body: { order: number }, |
||||||
|
) { |
||||||
|
return this.tablesService.reorderTable({ |
||||||
|
tableId, |
||||||
|
order: body.order, |
||||||
|
}); |
||||||
|
} |
||||||
} |
} |
||||||
|
@ -1,4 +1,487 @@ |
|||||||
import { Injectable } from '@nestjs/common'; |
import { Injectable } from '@nestjs/common'; |
||||||
|
import DOMPurify from 'isomorphic-dompurify'; |
||||||
|
import { |
||||||
|
AuditOperationSubTypes, |
||||||
|
AuditOperationTypes, |
||||||
|
ColumnType, |
||||||
|
isVirtualCol, |
||||||
|
ModelTypes, |
||||||
|
NormalColumnRequestType, |
||||||
|
TableReqType, |
||||||
|
UITypes, |
||||||
|
} from 'nocodb-sdk'; |
||||||
|
import ProjectMgrv2 from '../../db/sql-mgr/v2/ProjectMgrv2'; |
||||||
|
import { NcError } from '../../helpers/catchError'; |
||||||
|
import getColumnPropsFromUIDT from '../../helpers/getColumnPropsFromUIDT'; |
||||||
|
import getColumnUiType from '../../helpers/getColumnUiType'; |
||||||
|
import getTableNameAlias, { |
||||||
|
getColumnNameAlias, |
||||||
|
} from '../../helpers/getTableName'; |
||||||
|
import mapDefaultDisplayValue from '../../helpers/mapDefaultDisplayValue'; |
||||||
|
import { |
||||||
|
Audit, |
||||||
|
Column, |
||||||
|
LinkToAnotherRecordColumn, |
||||||
|
Model, |
||||||
|
ModelRoleVisibility, |
||||||
|
Project, |
||||||
|
User, |
||||||
|
View, |
||||||
|
} from '../../models'; |
||||||
|
import NcConnectionMgrv2 from '../../utils/common/NcConnectionMgrv2'; |
||||||
|
import { T } from 'nc-help'; |
||||||
|
import { validatePayload } from 'src/helpers'; |
||||||
|
|
||||||
@Injectable() |
@Injectable() |
||||||
export class TablesService {} |
export class TablesService { |
||||||
|
async tableUpdate(param: { |
||||||
|
tableId: any; |
||||||
|
table: TableReqType & { project_id?: string }; |
||||||
|
projectId?: string; |
||||||
|
}) { |
||||||
|
const model = await Model.get(param.tableId); |
||||||
|
|
||||||
|
const project = await Project.getWithInfo( |
||||||
|
param.table.project_id || param.projectId, |
||||||
|
); |
||||||
|
const base = project.bases.find((b) => b.id === model.base_id); |
||||||
|
|
||||||
|
if (model.project_id !== project.id) { |
||||||
|
NcError.badRequest('Model does not belong to project'); |
||||||
|
} |
||||||
|
|
||||||
|
// if meta present update meta and return
|
||||||
|
// todo: allow user to update meta and other prop in single api call
|
||||||
|
if ('meta' in param.table) { |
||||||
|
await Model.updateMeta(param.tableId, param.table.meta); |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
if (!param.table.table_name) { |
||||||
|
NcError.badRequest( |
||||||
|
'Missing table name `table_name` property in request body', |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
if (base.is_meta && project.prefix) { |
||||||
|
if (!param.table.table_name.startsWith(project.prefix)) { |
||||||
|
param.table.table_name = `${project.prefix}${param.table.table_name}`; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
param.table.table_name = DOMPurify.sanitize(param.table.table_name); |
||||||
|
|
||||||
|
// validate table name
|
||||||
|
if (/^\s+|\s+$/.test(param.table.table_name)) { |
||||||
|
NcError.badRequest( |
||||||
|
'Leading or trailing whitespace not allowed in table names', |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
if ( |
||||||
|
!(await Model.checkTitleAvailable({ |
||||||
|
table_name: param.table.table_name, |
||||||
|
project_id: project.id, |
||||||
|
base_id: base.id, |
||||||
|
})) |
||||||
|
) { |
||||||
|
NcError.badRequest('Duplicate table name'); |
||||||
|
} |
||||||
|
|
||||||
|
if (!param.table.title) { |
||||||
|
param.table.title = getTableNameAlias( |
||||||
|
param.table.table_name, |
||||||
|
project.prefix, |
||||||
|
base, |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
if ( |
||||||
|
!(await Model.checkAliasAvailable({ |
||||||
|
title: param.table.title, |
||||||
|
project_id: project.id, |
||||||
|
base_id: base.id, |
||||||
|
})) |
||||||
|
) { |
||||||
|
NcError.badRequest('Duplicate table alias'); |
||||||
|
} |
||||||
|
|
||||||
|
const sqlMgr = await ProjectMgrv2.getSqlMgr(project); |
||||||
|
const sqlClient = await NcConnectionMgrv2.getSqlClient(base); |
||||||
|
|
||||||
|
let tableNameLengthLimit = 255; |
||||||
|
const sqlClientType = sqlClient.knex.clientType(); |
||||||
|
if (sqlClientType === 'mysql2' || sqlClientType === 'mysql') { |
||||||
|
tableNameLengthLimit = 64; |
||||||
|
} else if (sqlClientType === 'pg') { |
||||||
|
tableNameLengthLimit = 63; |
||||||
|
} else if (sqlClientType === 'mssql') { |
||||||
|
tableNameLengthLimit = 128; |
||||||
|
} |
||||||
|
|
||||||
|
if (param.table.table_name.length > tableNameLengthLimit) { |
||||||
|
NcError.badRequest( |
||||||
|
`Table name exceeds ${tableNameLengthLimit} characters`, |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
await Model.updateAliasAndTableName( |
||||||
|
param.tableId, |
||||||
|
param.table.title, |
||||||
|
param.table.table_name, |
||||||
|
); |
||||||
|
|
||||||
|
await sqlMgr.sqlOpPlus(base, 'tableRename', { |
||||||
|
...param.table, |
||||||
|
tn: param.table.table_name, |
||||||
|
tn_old: model.table_name, |
||||||
|
}); |
||||||
|
|
||||||
|
T.emit('evt', { evt_type: 'table:updated' }); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
reorderTable(param: { tableId: string; order: any }) { |
||||||
|
return Model.updateOrder(param.tableId, param.order); |
||||||
|
} |
||||||
|
|
||||||
|
async tableDelete(param: { tableId: string; user: User; req?: any }) { |
||||||
|
const table = await Model.getByIdOrName({ id: param.tableId }); |
||||||
|
await table.getColumns(); |
||||||
|
|
||||||
|
const relationColumns = table.columns.filter( |
||||||
|
(c) => c.uidt === UITypes.LinkToAnotherRecord, |
||||||
|
); |
||||||
|
|
||||||
|
if (relationColumns?.length) { |
||||||
|
const referredTables = await Promise.all( |
||||||
|
relationColumns.map(async (c) => |
||||||
|
c |
||||||
|
.getColOptions<LinkToAnotherRecordColumn>() |
||||||
|
.then((opt) => opt.getRelatedTable()) |
||||||
|
.then(), |
||||||
|
), |
||||||
|
); |
||||||
|
NcError.badRequest( |
||||||
|
`Table can't be deleted since Table is being referred in following tables : ${referredTables.join( |
||||||
|
', ', |
||||||
|
)}. Delete LinkToAnotherRecord columns and try again.`,
|
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
const project = await Project.getWithInfo(table.project_id); |
||||||
|
const base = project.bases.find((b) => b.id === table.base_id); |
||||||
|
const sqlMgr = await ProjectMgrv2.getSqlMgr(project); |
||||||
|
(table as any).tn = table.table_name; |
||||||
|
table.columns = table.columns.filter((c) => !isVirtualCol(c)); |
||||||
|
table.columns.forEach((c) => { |
||||||
|
(c as any).cn = c.column_name; |
||||||
|
}); |
||||||
|
|
||||||
|
if (table.type === ModelTypes.TABLE) { |
||||||
|
await sqlMgr.sqlOpPlus(base, 'tableDelete', table); |
||||||
|
} else if (table.type === ModelTypes.VIEW) { |
||||||
|
await sqlMgr.sqlOpPlus(base, 'viewDelete', { |
||||||
|
...table, |
||||||
|
view_name: table.table_name, |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
await Audit.insert({ |
||||||
|
project_id: project.id, |
||||||
|
base_id: base.id, |
||||||
|
op_type: AuditOperationTypes.TABLE, |
||||||
|
op_sub_type: AuditOperationSubTypes.DELETED, |
||||||
|
user: param.user?.email, |
||||||
|
description: `Deleted ${table.type} ${table.table_name} with alias ${table.title} `, |
||||||
|
ip: param.req?.clientIp, |
||||||
|
}).then(() => {}); |
||||||
|
|
||||||
|
T.emit('evt', { evt_type: 'table:deleted' }); |
||||||
|
|
||||||
|
return table.delete(); |
||||||
|
} |
||||||
|
|
||||||
|
async getTableWithAccessibleViews(param: { tableId: string; user: User }) { |
||||||
|
const table = await Model.getWithInfo({ |
||||||
|
id: param.tableId, |
||||||
|
}); |
||||||
|
|
||||||
|
// todo: optimise
|
||||||
|
const viewList = <View[]>( |
||||||
|
await this.xcVisibilityMetaGet(table.project_id, [table]) |
||||||
|
); |
||||||
|
|
||||||
|
//await View.list(param.tableId)
|
||||||
|
table.views = viewList.filter((table: any) => { |
||||||
|
return Object.keys(param.user?.roles).some( |
||||||
|
(role) => param.user?.roles[role] && !table.disabled[role], |
||||||
|
); |
||||||
|
}); |
||||||
|
|
||||||
|
return table; |
||||||
|
} |
||||||
|
|
||||||
|
async xcVisibilityMetaGet( |
||||||
|
projectId, |
||||||
|
_models: Model[] = null, |
||||||
|
includeM2M = true, |
||||||
|
// type: 'table' | 'tableAndViews' | 'views' = 'table'
|
||||||
|
) { |
||||||
|
// todo: move to
|
||||||
|
const roles = [ |
||||||
|
'owner', |
||||||
|
'creator', |
||||||
|
'viewer', |
||||||
|
'editor', |
||||||
|
'commenter', |
||||||
|
'guest', |
||||||
|
]; |
||||||
|
|
||||||
|
const defaultDisabled = roles.reduce((o, r) => ({ ...o, [r]: false }), {}); |
||||||
|
|
||||||
|
let models = |
||||||
|
_models || |
||||||
|
(await Model.list({ |
||||||
|
project_id: projectId, |
||||||
|
base_id: undefined, |
||||||
|
})); |
||||||
|
|
||||||
|
models = includeM2M ? models : (models.filter((t) => !t.mm) as Model[]); |
||||||
|
|
||||||
|
const result = await models.reduce(async (_obj, model) => { |
||||||
|
const obj = await _obj; |
||||||
|
|
||||||
|
const views = await model.getViews(); |
||||||
|
for (const view of views) { |
||||||
|
obj[view.id] = { |
||||||
|
ptn: model.table_name, |
||||||
|
_ptn: model.title, |
||||||
|
ptype: model.type, |
||||||
|
tn: view.title, |
||||||
|
_tn: view.title, |
||||||
|
table_meta: model.meta, |
||||||
|
...view, |
||||||
|
disabled: { ...defaultDisabled }, |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
return obj; |
||||||
|
}, Promise.resolve({})); |
||||||
|
|
||||||
|
const disabledList = await ModelRoleVisibility.list(projectId); |
||||||
|
|
||||||
|
for (const d of disabledList) { |
||||||
|
if (result[d.fk_view_id]) |
||||||
|
result[d.fk_view_id].disabled[d.role] = !!d.disabled; |
||||||
|
} |
||||||
|
|
||||||
|
return Object.values(result); |
||||||
|
} |
||||||
|
|
||||||
|
async getAccessibleTables(param: { |
||||||
|
projectId: string; |
||||||
|
baseId: string; |
||||||
|
includeM2M?: boolean; |
||||||
|
roles: Record<string, boolean>; |
||||||
|
}) { |
||||||
|
const viewList = await this.xcVisibilityMetaGet(param.projectId); |
||||||
|
|
||||||
|
// todo: optimise
|
||||||
|
const tableViewMapping = viewList.reduce((o, view: any) => { |
||||||
|
o[view.fk_model_id] = o[view.fk_model_id] || 0; |
||||||
|
if ( |
||||||
|
Object.keys(param.roles).some( |
||||||
|
(role) => param.roles[role] && !view.disabled[role], |
||||||
|
) |
||||||
|
) { |
||||||
|
o[view.fk_model_id]++; |
||||||
|
} |
||||||
|
return o; |
||||||
|
}, {}); |
||||||
|
|
||||||
|
const tableList = ( |
||||||
|
await Model.list({ |
||||||
|
project_id: param.projectId, |
||||||
|
base_id: param.baseId, |
||||||
|
}) |
||||||
|
).filter((t) => tableViewMapping[t.id]); |
||||||
|
|
||||||
|
return param.includeM2M |
||||||
|
? tableList |
||||||
|
: (tableList.filter((t) => !t.mm) as Model[]); |
||||||
|
} |
||||||
|
|
||||||
|
async tableCreate(param: { |
||||||
|
projectId: string; |
||||||
|
baseId?: string; |
||||||
|
table: TableReqType; |
||||||
|
user: User; |
||||||
|
req?: any; |
||||||
|
}) { |
||||||
|
validatePayload('swagger.json#/components/schemas/TableReq', param.table); |
||||||
|
|
||||||
|
const tableCreatePayLoad: Omit<TableReqType, 'columns'> & { |
||||||
|
columns: (Omit<ColumnType, 'column_name' | 'title'> & { cn?: string })[]; |
||||||
|
} = { |
||||||
|
...param.table, |
||||||
|
}; |
||||||
|
|
||||||
|
const project = await Project.getWithInfo(param.projectId); |
||||||
|
let base = project.bases[0]; |
||||||
|
|
||||||
|
if (param.baseId) { |
||||||
|
base = project.bases.find((b) => b.id === param.baseId); |
||||||
|
} |
||||||
|
|
||||||
|
if ( |
||||||
|
!tableCreatePayLoad.table_name || |
||||||
|
(project.prefix && project.prefix === tableCreatePayLoad.table_name) |
||||||
|
) { |
||||||
|
NcError.badRequest( |
||||||
|
'Missing table name `table_name` property in request body', |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
if (base.is_meta && project.prefix) { |
||||||
|
if (!tableCreatePayLoad.table_name.startsWith(project.prefix)) { |
||||||
|
tableCreatePayLoad.table_name = `${project.prefix}_${tableCreatePayLoad.table_name}`; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
tableCreatePayLoad.table_name = DOMPurify.sanitize( |
||||||
|
tableCreatePayLoad.table_name, |
||||||
|
); |
||||||
|
|
||||||
|
// validate table name
|
||||||
|
if (/^\s+|\s+$/.test(tableCreatePayLoad.table_name)) { |
||||||
|
NcError.badRequest( |
||||||
|
'Leading or trailing whitespace not allowed in table names', |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
if ( |
||||||
|
!(await Model.checkTitleAvailable({ |
||||||
|
table_name: tableCreatePayLoad.table_name, |
||||||
|
project_id: project.id, |
||||||
|
base_id: base.id, |
||||||
|
})) |
||||||
|
) { |
||||||
|
NcError.badRequest('Duplicate table name'); |
||||||
|
} |
||||||
|
|
||||||
|
if (!tableCreatePayLoad.title) { |
||||||
|
tableCreatePayLoad.title = getTableNameAlias( |
||||||
|
tableCreatePayLoad.table_name, |
||||||
|
project.prefix, |
||||||
|
base, |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
if ( |
||||||
|
!(await Model.checkAliasAvailable({ |
||||||
|
title: tableCreatePayLoad.title, |
||||||
|
project_id: project.id, |
||||||
|
base_id: base.id, |
||||||
|
})) |
||||||
|
) { |
||||||
|
NcError.badRequest('Duplicate table alias'); |
||||||
|
} |
||||||
|
|
||||||
|
const sqlMgr = await ProjectMgrv2.getSqlMgr(project); |
||||||
|
|
||||||
|
const sqlClient = await NcConnectionMgrv2.getSqlClient(base); |
||||||
|
|
||||||
|
let tableNameLengthLimit = 255; |
||||||
|
const sqlClientType = sqlClient.knex.clientType(); |
||||||
|
if (sqlClientType === 'mysql2' || sqlClientType === 'mysql') { |
||||||
|
tableNameLengthLimit = 64; |
||||||
|
} else if (sqlClientType === 'pg') { |
||||||
|
tableNameLengthLimit = 63; |
||||||
|
} else if (sqlClientType === 'mssql') { |
||||||
|
tableNameLengthLimit = 128; |
||||||
|
} |
||||||
|
|
||||||
|
if (tableCreatePayLoad.table_name.length > tableNameLengthLimit) { |
||||||
|
NcError.badRequest( |
||||||
|
`Table name exceeds ${tableNameLengthLimit} characters`, |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
const mxColumnLength = Column.getMaxColumnNameLength(sqlClientType); |
||||||
|
|
||||||
|
for (const column of param.table.columns) { |
||||||
|
if (column.column_name.length > mxColumnLength) { |
||||||
|
NcError.badRequest( |
||||||
|
`Column name ${column.column_name} exceeds ${mxColumnLength} characters`, |
||||||
|
); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
tableCreatePayLoad.columns = await Promise.all( |
||||||
|
param.table.columns?.map(async (c) => ({ |
||||||
|
...(await getColumnPropsFromUIDT(c as any, base)), |
||||||
|
cn: c.column_name, |
||||||
|
column_name: c.column_name, |
||||||
|
})), |
||||||
|
); |
||||||
|
await sqlMgr.sqlOpPlus(base, 'tableCreate', { |
||||||
|
...tableCreatePayLoad, |
||||||
|
tn: tableCreatePayLoad.table_name, |
||||||
|
}); |
||||||
|
|
||||||
|
const columns: Array< |
||||||
|
Omit<Column, 'column_name' | 'title'> & { |
||||||
|
cn: string; |
||||||
|
system?: boolean; |
||||||
|
} |
||||||
|
> = (await sqlClient.columnList({ tn: tableCreatePayLoad.table_name })) |
||||||
|
?.data?.list; |
||||||
|
|
||||||
|
const tables = await Model.list({ |
||||||
|
project_id: project.id, |
||||||
|
base_id: base.id, |
||||||
|
}); |
||||||
|
|
||||||
|
await Audit.insert({ |
||||||
|
project_id: project.id, |
||||||
|
base_id: base.id, |
||||||
|
op_type: AuditOperationTypes.TABLE, |
||||||
|
op_sub_type: AuditOperationSubTypes.CREATED, |
||||||
|
user: param.user?.email, |
||||||
|
description: `created table ${tableCreatePayLoad.table_name} with alias ${tableCreatePayLoad.title} `, |
||||||
|
ip: param.req?.clientIp, |
||||||
|
}).then(() => {}); |
||||||
|
|
||||||
|
mapDefaultDisplayValue(param.table.columns); |
||||||
|
|
||||||
|
T.emit('evt', { evt_type: 'table:created' }); |
||||||
|
|
||||||
|
// todo: type correction
|
||||||
|
const result = await Model.insert(project.id, base.id, { |
||||||
|
...tableCreatePayLoad, |
||||||
|
columns: columns.map((c, i) => { |
||||||
|
const colMetaFromReq = param.table?.columns?.find( |
||||||
|
(c1) => c.cn === c1.column_name, |
||||||
|
); |
||||||
|
return { |
||||||
|
...colMetaFromReq, |
||||||
|
uidt: colMetaFromReq?.uidt || c.uidt || getColumnUiType(base, c), |
||||||
|
...c, |
||||||
|
dtxp: [UITypes.MultiSelect, UITypes.SingleSelect].includes( |
||||||
|
colMetaFromReq.uidt as any, |
||||||
|
) |
||||||
|
? colMetaFromReq.dtxp |
||||||
|
: c.dtxp, |
||||||
|
title: colMetaFromReq?.title || getColumnNameAlias(c.cn, base), |
||||||
|
column_name: c.cn, |
||||||
|
order: i + 1, |
||||||
|
} as NormalColumnRequestType; |
||||||
|
}), |
||||||
|
order: +(tables?.pop()?.order ?? 0) + 1, |
||||||
|
} as any); |
||||||
|
|
||||||
|
return result; |
||||||
|
} |
||||||
|
} |
||||||
|
Loading…
Reference in new issue