Browse Source

refactor: move populateMeta to helpers

Signed-off-by: mertmit <mertmit99@gmail.com>
pull/5060/head
mertmit 2 years ago
parent
commit
328c547c7f
  1. 263
      packages/nocodb/src/lib/meta/api/baseApis.ts
  2. 3
      packages/nocodb/src/lib/meta/api/helpers/index.ts
  3. 263
      packages/nocodb/src/lib/meta/api/helpers/populateMeta.ts
  4. 263
      packages/nocodb/src/lib/meta/api/projectApis.ts

263
packages/nocodb/src/lib/meta/api/baseApis.ts

@ -1,22 +1,13 @@
import { Request, Response } from 'express';
import Project from '../../models/Project';
import { BaseListType, ModelTypes, UITypes } from 'nocodb-sdk';
import { BaseListType } from 'nocodb-sdk';
import { PagedResponseImpl } from '../helpers/PagedResponse';
import { syncBaseMigration } from '../helpers/syncMigration';
import { IGNORE_TABLES } from '../../utils/common/BaseApiBuilder';
import Column from '../../models/Column';
import Model from '../../models/Model';
import NcHelp from '../../utils/NcHelp';
import Base from '../../models/Base';
import NcConnectionMgrv2 from '../../utils/common/NcConnectionMgrv2';
import getTableNameAlias, { getColumnNameAlias } from '../helpers/getTableName';
import LinkToAnotherRecordColumn from '../../models/LinkToAnotherRecordColumn';
import ncMetaAclMw from '../helpers/ncMetaAclMw';
import { Tele } from 'nc-help';
import getColumnUiType from '../helpers/getColumnUiType';
import mapDefaultPrimaryValue from '../helpers/mapDefaultPrimaryValue';
import { extractAndGenerateManyToManyRelations } from './metaDiffApis';
import { metaApiMetrics } from '../helpers/apiMetrics';
import { populateMeta } from './helpers';
export async function baseGet(
req: Request<any, any, any>,
@ -107,256 +98,6 @@ async function baseCreate(req: Request<any, any>, res) {
res.json(base);
}
async function populateMeta(base: Base, project: Project): Promise<any> {
const info = {
type: 'rest',
apiCount: 0,
tablesCount: 0,
relationsCount: 0,
viewsCount: 0,
client: base?.getConnectionConfig()?.client,
timeTaken: 0,
};
const t = process.hrtime();
const sqlClient = await NcConnectionMgrv2.getSqlClient(base);
let order = 1;
const models2: { [tableName: string]: Model } = {};
const virtualColumnsInsert = [];
/* Get all relations */
const relations = (await sqlClient.relationListAll())?.data?.list;
info.relationsCount = relations.length;
let tables = (await sqlClient.tableList())?.data?.list
?.filter(({ tn }) => !IGNORE_TABLES.includes(tn))
?.map((t) => {
t.order = ++order;
t.title = getTableNameAlias(t.tn, project.prefix, base);
t.table_name = t.tn;
return t;
});
/* filter based on prefix */
if (base.is_meta && project?.prefix) {
tables = tables.filter((t) => {
return t?.tn?.startsWith(project?.prefix);
});
}
info.tablesCount = tables.length;
tables.forEach((t) => {
t.title = getTableNameAlias(t.tn, project.prefix, base);
});
relations.forEach((r) => {
r.title = getTableNameAlias(r.tn, project.prefix, base);
r.rtitle = getTableNameAlias(r.rtn, project.prefix, base);
});
// await this.syncRelations();
const tableMetasInsert = tables.map((table) => {
return async () => {
/* filter relation where this table is present */
const tableRelations = relations.filter(
(r) => r.tn === table.tn || r.rtn === table.tn
);
const columns: Array<
Omit<Column, 'column_name' | 'title'> & {
cn: string;
system?: boolean;
}
> = (await sqlClient.columnList({ tn: table.tn }))?.data?.list;
const hasMany =
table.type === 'view'
? []
: tableRelations.filter((r) => r.rtn === table.tn);
const belongsTo =
table.type === 'view'
? []
: tableRelations.filter((r) => r.tn === table.tn);
mapDefaultPrimaryValue(columns);
// add vitual columns
const virtualColumns = [
...hasMany.map((hm) => {
return {
uidt: UITypes.LinkToAnotherRecord,
type: 'hm',
hm,
title: `${hm.title} List`,
};
}),
...belongsTo.map((bt) => {
// find and mark foreign key column
const fkColumn = columns.find((c) => c.cn === bt.cn);
if (fkColumn) {
fkColumn.uidt = UITypes.ForeignKey;
fkColumn.system = true;
}
return {
uidt: UITypes.LinkToAnotherRecord,
type: 'bt',
bt,
title: `${bt.rtitle}`,
};
}),
];
// await Model.insert(project.id, base.id, meta);
/* create nc_models and its rows if it doesn't exists */
models2[table.table_name] = await Model.insert(project.id, base.id, {
table_name: table.tn || table.table_name,
title: table.title,
type: table.type || 'table',
order: table.order,
});
// table crud apis
info.apiCount += 5;
let colOrder = 1;
for (const column of columns) {
await Column.insert({
uidt: column.uidt || getColumnUiType(base, column),
fk_model_id: models2[table.tn].id,
...column,
title: getColumnNameAlias(column.cn, base),
column_name: column.cn,
order: colOrder++,
});
}
virtualColumnsInsert.push(async () => {
const columnNames = {};
for (const column of virtualColumns) {
// generate unique name if there is any duplicate column name
let c = 0;
while (`${column.title}${c || ''}` in columnNames) {
c++;
}
column.title = `${column.title}${c || ''}`;
columnNames[column.title] = true;
const rel = column.hm || column.bt;
const rel_column_id = (await models2?.[rel.tn]?.getColumns())?.find(
(c) => c.column_name === rel.cn
)?.id;
const tnId = models2?.[rel.tn]?.id;
const ref_rel_column_id = (
await models2?.[rel.rtn]?.getColumns()
)?.find((c) => c.column_name === rel.rcn)?.id;
const rtnId = models2?.[rel.rtn]?.id;
try {
await Column.insert<LinkToAnotherRecordColumn>({
project_id: project.id,
db_alias: base.id,
fk_model_id: models2[table.tn].id,
cn: column.cn,
title: column.title,
uidt: column.uidt,
type: column.hm ? 'hm' : column.mm ? 'mm' : 'bt',
// column_id,
fk_child_column_id: rel_column_id,
fk_parent_column_id: ref_rel_column_id,
fk_index_name: rel.fkn,
ur: rel.ur,
dr: rel.dr,
order: colOrder++,
fk_related_model_id: column.hm ? tnId : rtnId,
system: column.system,
});
// nested relations data apis
info.apiCount += 5;
} catch (e) {
console.log(e);
}
}
});
};
});
/* handle xc_tables update in parallel */
await NcHelp.executeOperations(tableMetasInsert, base.type);
await NcHelp.executeOperations(virtualColumnsInsert, base.type);
await extractAndGenerateManyToManyRelations(Object.values(models2));
let views: Array<{ order: number; table_name: string; title: string }> = (
await sqlClient.viewList()
)?.data?.list
// ?.filter(({ tn }) => !IGNORE_TABLES.includes(tn))
?.map((v) => {
v.order = ++order;
v.table_name = v.view_name;
v.title = getTableNameAlias(v.view_name, project.prefix, base);
return v;
});
/* filter based on prefix */
if (base.is_meta && project?.prefix) {
views = tables.filter((t) => {
return t?.tn?.startsWith(project?.prefix);
});
}
info.viewsCount = views.length;
const viewMetasInsert = views.map((table) => {
return async () => {
const columns = (await sqlClient.columnList({ tn: table.table_name }))
?.data?.list;
/* create nc_models and its rows if it doesn't exists */
models2[table.table_name] = await Model.insert(project.id, base.id, {
table_name: table.table_name,
title: getTableNameAlias(table.table_name, project.prefix, base),
// todo: sanitize
type: ModelTypes.VIEW,
order: table.order,
});
let colOrder = 1;
// view apis
info.apiCount += 2;
for (const column of columns) {
await Column.insert({
fk_model_id: models2[table.table_name].id,
...column,
title: getColumnNameAlias(column.cn, base),
order: colOrder++,
uidt: getColumnUiType(base, column),
});
}
};
});
await NcHelp.executeOperations(viewMetasInsert, base.type);
const t1 = process.hrtime(t);
const t2 = t1[0] + t1[1] / 1000000000;
(info as any).timeTaken = t2.toFixed(1);
return info;
}
export default (router) => {
router.get(
'/api/v1/db/meta/projects/:projectId/bases/:baseId',

3
packages/nocodb/src/lib/meta/api/helpers/index.ts

@ -0,0 +1,3 @@
import { populateMeta } from "./populateMeta";
export { populateMeta }

263
packages/nocodb/src/lib/meta/api/helpers/populateMeta.ts

@ -0,0 +1,263 @@
import Project from '../../../models/Project';
import Column from '../../../models/Column';
import Model from '../../../models/Model';
import NcHelp from '../../../utils/NcHelp';
import Base from '../../../models/Base';
import NcConnectionMgrv2 from '../../../utils/common/NcConnectionMgrv2';
import getTableNameAlias, { getColumnNameAlias } from '../../helpers/getTableName';
import LinkToAnotherRecordColumn from '../../../models/LinkToAnotherRecordColumn';
import getColumnUiType from '../../helpers/getColumnUiType';
import mapDefaultPrimaryValue from '../../helpers/mapDefaultPrimaryValue';
import { extractAndGenerateManyToManyRelations } from '../metaDiffApis';
import { ModelTypes, UITypes } from 'nocodb-sdk';
import { IGNORE_TABLES } from '../../../utils/common/BaseApiBuilder';
export async function populateMeta(base: Base, project: Project): Promise<any> {
const info = {
type: 'rest',
apiCount: 0,
tablesCount: 0,
relationsCount: 0,
viewsCount: 0,
client: base?.getConnectionConfig()?.client,
timeTaken: 0,
};
const t = process.hrtime();
const sqlClient = NcConnectionMgrv2.getSqlClient(base);
let order = 1;
const models2: { [tableName: string]: Model } = {};
const virtualColumnsInsert = [];
/* Get all relations */
const relations = (await sqlClient.relationListAll())?.data?.list;
info.relationsCount = relations.length;
let tables = (await sqlClient.tableList())?.data?.list
?.filter(({ tn }) => !IGNORE_TABLES.includes(tn))
?.map((t) => {
t.order = ++order;
t.title = getTableNameAlias(t.tn, project.prefix, base);
t.table_name = t.tn;
return t;
});
/* filter based on prefix */
if (base.is_meta && project?.prefix) {
tables = tables.filter((t) => {
return t?.tn?.startsWith(project?.prefix);
});
}
info.tablesCount = tables.length;
tables.forEach((t) => {
t.title = getTableNameAlias(t.tn, project.prefix, base);
});
relations.forEach((r) => {
r.title = getTableNameAlias(r.tn, project.prefix, base);
r.rtitle = getTableNameAlias(r.rtn, project.prefix, base);
});
// await this.syncRelations();
const tableMetasInsert = tables.map((table) => {
return async () => {
/* filter relation where this table is present */
const tableRelations = relations.filter(
(r) => r.tn === table.tn || r.rtn === table.tn
);
const columns: Array<
Omit<Column, 'column_name' | 'title'> & {
cn: string;
system?: boolean;
}
> = (await sqlClient.columnList({ tn: table.tn }))?.data?.list;
const hasMany =
table.type === 'view'
? []
: tableRelations.filter((r) => r.rtn === table.tn);
const belongsTo =
table.type === 'view'
? []
: tableRelations.filter((r) => r.tn === table.tn);
mapDefaultPrimaryValue(columns);
// add vitual columns
const virtualColumns = [
...hasMany.map((hm) => {
return {
uidt: UITypes.LinkToAnotherRecord,
type: 'hm',
hm,
title: `${hm.title} List`,
};
}),
...belongsTo.map((bt) => {
// find and mark foreign key column
const fkColumn = columns.find((c) => c.cn === bt.cn);
if (fkColumn) {
fkColumn.uidt = UITypes.ForeignKey;
fkColumn.system = true;
}
return {
uidt: UITypes.LinkToAnotherRecord,
type: 'bt',
bt,
title: `${bt.rtitle}`,
};
}),
];
// await Model.insert(project.id, base.id, meta);
/* create nc_models and its rows if it doesn't exists */
models2[table.table_name] = await Model.insert(project.id, base.id, {
table_name: table.tn || table.table_name,
title: table.title,
type: table.type || 'table',
order: table.order,
});
// table crud apis
info.apiCount += 5;
let colOrder = 1;
for (const column of columns) {
await Column.insert({
uidt: column.uidt || getColumnUiType(base, column),
fk_model_id: models2[table.tn].id,
...column,
title: getColumnNameAlias(column.cn, base),
column_name: column.cn,
order: colOrder++,
});
}
virtualColumnsInsert.push(async () => {
const columnNames = {};
for (const column of virtualColumns) {
// generate unique name if there is any duplicate column name
let c = 0;
while (`${column.title}${c || ''}` in columnNames) {
c++;
}
column.title = `${column.title}${c || ''}`;
columnNames[column.title] = true;
const rel = column.hm || column.bt;
const rel_column_id = (await models2?.[rel.tn]?.getColumns())?.find(
(c) => c.column_name === rel.cn
)?.id;
const tnId = models2?.[rel.tn]?.id;
const ref_rel_column_id = (
await models2?.[rel.rtn]?.getColumns()
)?.find((c) => c.column_name === rel.rcn)?.id;
const rtnId = models2?.[rel.rtn]?.id;
try {
await Column.insert<LinkToAnotherRecordColumn>({
project_id: project.id,
db_alias: base.id,
fk_model_id: models2[table.tn].id,
cn: column.cn,
title: column.title,
uidt: column.uidt,
type: column.hm ? 'hm' : column.mm ? 'mm' : 'bt',
// column_id,
fk_child_column_id: rel_column_id,
fk_parent_column_id: ref_rel_column_id,
fk_index_name: rel.fkn,
ur: rel.ur,
dr: rel.dr,
order: colOrder++,
fk_related_model_id: column.hm ? tnId : rtnId,
system: column.system,
});
// nested relations data apis
info.apiCount += 5;
} catch (e) {
console.log(e);
}
}
});
};
});
/* handle xc_tables update in parallel */
await NcHelp.executeOperations(tableMetasInsert, base.type);
await NcHelp.executeOperations(virtualColumnsInsert, base.type);
await extractAndGenerateManyToManyRelations(Object.values(models2));
let views: Array<{ order: number; table_name: string; title: string }> = (
await sqlClient.viewList()
)?.data?.list
// ?.filter(({ tn }) => !IGNORE_TABLES.includes(tn))
?.map((v) => {
v.order = ++order;
v.table_name = v.view_name;
v.title = getTableNameAlias(v.view_name, project.prefix, base);
return v;
});
/* filter based on prefix */
if (base.is_meta && project?.prefix) {
views = tables.filter((t) => {
return t?.tn?.startsWith(project?.prefix);
});
}
info.viewsCount = views.length;
const viewMetasInsert = views.map((table) => {
return async () => {
const columns = (await sqlClient.columnList({ tn: table.table_name }))
?.data?.list;
/* create nc_models and its rows if it doesn't exists */
models2[table.table_name] = await Model.insert(project.id, base.id, {
table_name: table.table_name,
title: getTableNameAlias(table.table_name, project.prefix, base),
// todo: sanitize
type: ModelTypes.VIEW,
order: table.order,
});
let colOrder = 1;
// view apis
info.apiCount += 2;
for (const column of columns) {
await Column.insert({
fk_model_id: models2[table.table_name].id,
...column,
title: getColumnNameAlias(column.cn, base),
order: colOrder++,
uidt: getColumnUiType(base, column),
});
}
};
});
await NcHelp.executeOperations(viewMetasInsert, base.type);
const t1 = process.hrtime(t);
const t2 = t1[0] + t1[1] / 1000000000;
(info as any).timeTaken = t2.toFixed(1);
return info;
}

263
packages/nocodb/src/lib/meta/api/projectApis.ts

@ -1,33 +1,24 @@
import { Request, Response } from 'express';
import { OrgUserRoles, ProjectType } from 'nocodb-sdk';
import Project from '../../models/Project';
import { ModelTypes, ProjectListType, UITypes } from 'nocodb-sdk';
import { ProjectListType } from 'nocodb-sdk';
import DOMPurify from 'isomorphic-dompurify';
import { packageVersion } from '../../utils/packageVersion';
import { Tele } from 'nc-help';
import { PagedResponseImpl } from '../helpers/PagedResponse';
import syncMigration from '../helpers/syncMigration';
import { IGNORE_TABLES } from '../../utils/common/BaseApiBuilder';
import Column from '../../models/Column';
import Model from '../../models/Model';
import NcHelp from '../../utils/NcHelp';
import Base from '../../models/Base';
import NcConnectionMgrv2 from '../../utils/common/NcConnectionMgrv2';
import getTableNameAlias, { getColumnNameAlias } from '../helpers/getTableName';
import LinkToAnotherRecordColumn from '../../models/LinkToAnotherRecordColumn';
import ncMetaAclMw from '../helpers/ncMetaAclMw';
import ProjectUser from '../../models/ProjectUser';
import { customAlphabet } from 'nanoid';
import Noco from '../../Noco';
import isDocker from 'is-docker';
import { NcError } from '../helpers/catchError';
import getColumnUiType from '../helpers/getColumnUiType';
import mapDefaultPrimaryValue from '../helpers/mapDefaultPrimaryValue';
import { extractAndGenerateManyToManyRelations } from './metaDiffApis';
import { metaApiMetrics } from '../helpers/apiMetrics';
import { extractPropsAndSanitize } from '../helpers/extractProps';
import NcConfigFactory from '../../utils/NcConfigFactory';
import { promisify } from 'util';
import { populateMeta } from './helpers';
const nanoid = customAlphabet('1234567890abcdefghijklmnopqrstuvwxyz_', 4);
@ -202,256 +193,6 @@ async function projectCreate(req: Request<any, any>, res) {
res.json(project);
}
async function populateMeta(base: Base, project: Project): Promise<any> {
const info = {
type: 'rest',
apiCount: 0,
tablesCount: 0,
relationsCount: 0,
viewsCount: 0,
client: base?.getConnectionConfig()?.client,
timeTaken: 0,
};
const t = process.hrtime();
const sqlClient = await NcConnectionMgrv2.getSqlClient(base);
let order = 1;
const models2: { [tableName: string]: Model } = {};
const virtualColumnsInsert = [];
/* Get all relations */
const relations = (await sqlClient.relationListAll())?.data?.list;
info.relationsCount = relations.length;
let tables = (await sqlClient.tableList())?.data?.list
?.filter(({ tn }) => !IGNORE_TABLES.includes(tn))
?.map((t) => {
t.order = ++order;
t.title = getTableNameAlias(t.tn, project.prefix, base);
t.table_name = t.tn;
return t;
});
/* filter based on prefix */
if (project?.prefix) {
tables = tables.filter((t) => {
return t?.tn?.startsWith(project?.prefix);
});
}
info.tablesCount = tables.length;
tables.forEach((t) => {
t.title = getTableNameAlias(t.tn, project.prefix, base);
});
relations.forEach((r) => {
r.title = getTableNameAlias(r.tn, project.prefix, base);
r.rtitle = getTableNameAlias(r.rtn, project.prefix, base);
});
// await this.syncRelations();
const tableMetasInsert = tables.map((table) => {
return async () => {
/* filter relation where this table is present */
const tableRelations = relations.filter(
(r) => r.tn === table.tn || r.rtn === table.tn
);
const columns: Array<
Omit<Column, 'column_name' | 'title'> & {
cn: string;
system?: boolean;
}
> = (await sqlClient.columnList({ tn: table.tn }))?.data?.list;
const hasMany =
table.type === 'view'
? []
: tableRelations.filter((r) => r.rtn === table.tn);
const belongsTo =
table.type === 'view'
? []
: tableRelations.filter((r) => r.tn === table.tn);
mapDefaultPrimaryValue(columns);
// add vitual columns
const virtualColumns = [
...hasMany.map((hm) => {
return {
uidt: UITypes.LinkToAnotherRecord,
type: 'hm',
hm,
title: `${hm.title} List`,
};
}),
...belongsTo.map((bt) => {
// find and mark foreign key column
const fkColumn = columns.find((c) => c.cn === bt.cn);
if (fkColumn) {
fkColumn.uidt = UITypes.ForeignKey;
fkColumn.system = true;
}
return {
uidt: UITypes.LinkToAnotherRecord,
type: 'bt',
bt,
title: `${bt.rtitle}`,
};
}),
];
// await Model.insert(project.id, base.id, meta);
/* create nc_models and its rows if it doesn't exists */
models2[table.table_name] = await Model.insert(project.id, base.id, {
table_name: table.tn || table.table_name,
title: table.title,
type: table.type || 'table',
order: table.order,
});
// table crud apis
info.apiCount += 5;
let colOrder = 1;
for (const column of columns) {
await Column.insert({
uidt: column.uidt || getColumnUiType(base, column),
fk_model_id: models2[table.tn].id,
...column,
title: getColumnNameAlias(column.cn, base),
column_name: column.cn,
order: colOrder++,
});
}
virtualColumnsInsert.push(async () => {
const columnNames = {};
for (const column of virtualColumns) {
// generate unique name if there is any duplicate column name
let c = 0;
while (`${column.title}${c || ''}` in columnNames) {
c++;
}
column.title = `${column.title}${c || ''}`;
columnNames[column.title] = true;
const rel = column.hm || column.bt;
const rel_column_id = (await models2?.[rel.tn]?.getColumns())?.find(
(c) => c.column_name === rel.cn
)?.id;
const tnId = models2?.[rel.tn]?.id;
const ref_rel_column_id = (
await models2?.[rel.rtn]?.getColumns()
)?.find((c) => c.column_name === rel.rcn)?.id;
const rtnId = models2?.[rel.rtn]?.id;
try {
await Column.insert<LinkToAnotherRecordColumn>({
project_id: project.id,
db_alias: base.id,
fk_model_id: models2[table.tn].id,
cn: column.cn,
title: column.title,
uidt: column.uidt,
type: column.hm ? 'hm' : column.mm ? 'mm' : 'bt',
// column_id,
fk_child_column_id: rel_column_id,
fk_parent_column_id: ref_rel_column_id,
fk_index_name: rel.fkn,
ur: rel.ur,
dr: rel.dr,
order: colOrder++,
fk_related_model_id: column.hm ? tnId : rtnId,
system: column.system,
});
// nested relations data apis
info.apiCount += 5;
} catch (e) {
console.log(e);
}
}
});
};
});
/* handle xc_tables update in parallel */
await NcHelp.executeOperations(tableMetasInsert, base.type);
await NcHelp.executeOperations(virtualColumnsInsert, base.type);
await extractAndGenerateManyToManyRelations(Object.values(models2));
let views: Array<{ order: number; table_name: string; title: string }> = (
await sqlClient.viewList()
)?.data?.list
// ?.filter(({ tn }) => !IGNORE_TABLES.includes(tn))
?.map((v) => {
v.order = ++order;
v.table_name = v.view_name;
v.title = getTableNameAlias(v.view_name, project.prefix, base);
return v;
});
/* filter based on prefix */
if (project?.prefix) {
views = tables.filter((t) => {
return t?.tn?.startsWith(project?.prefix);
});
}
info.viewsCount = views.length;
const viewMetasInsert = views.map((table) => {
return async () => {
const columns = (await sqlClient.columnList({ tn: table.table_name }))
?.data?.list;
/* create nc_models and its rows if it doesn't exists */
models2[table.table_name] = await Model.insert(project.id, base.id, {
table_name: table.table_name,
title: getTableNameAlias(table.table_name, project.prefix, base),
// todo: sanitize
type: ModelTypes.VIEW,
order: table.order,
});
let colOrder = 1;
// view apis
info.apiCount += 2;
for (const column of columns) {
await Column.insert({
fk_model_id: models2[table.table_name].id,
...column,
title: getColumnNameAlias(column.cn, base),
order: colOrder++,
uidt: getColumnUiType(base, column),
});
}
};
});
await NcHelp.executeOperations(viewMetasInsert, base.type);
const t1 = process.hrtime(t);
const t2 = t1[0] + t1[1] / 1000000000;
(info as any).timeTaken = t2.toFixed(1);
return info;
}
export async function projectInfoGet(_req, res) {
res.json({
Node: process.version,

Loading…
Cancel
Save