Browse Source

feat(wip): back-end support for multiple base

Signed-off-by: mertmit <mertmit99@gmail.com>
pull/3573/head
mertmit 2 years ago
parent
commit
3c2c7ef3ff
  1. 1
      packages/nocodb/src/lib/meta/api/dataApis/helpers.ts
  2. 1
      packages/nocodb/src/lib/meta/api/dataApis/oldDataApis.ts
  3. 364
      packages/nocodb/src/lib/meta/api/metaDiffApis.ts
  4. 6
      packages/nocodb/src/lib/meta/api/modelVisibilityApis.ts
  5. 40
      packages/nocodb/src/lib/meta/api/projectApis.ts
  6. 62
      packages/nocodb/src/lib/models/Model.ts

1
packages/nocodb/src/lib/meta/api/dataApis/helpers.ts

@ -24,7 +24,6 @@ export async function getViewAndModelFromRequestByAliasOrId(
const model = await Model.getByAliasOrId({
project_id: project.id,
base_id: project.bases?.[0]?.id,
aliasOrId: req.params.tableName,
});
const view =

1
packages/nocodb/src/lib/meta/api/dataApis/oldDataApis.ts

@ -108,7 +108,6 @@ async function getViewAndModelFromRequest(req) {
const project = await Project.getWithInfo(req.params.projectId);
const model = await Model.getByAliasOrId({
project_id: project.id,
base_id: project.bases?.[0]?.id,
aliasOrId: req.params.tableName,
});
const view =

364
packages/nocodb/src/lib/meta/api/metaDiffApis.ts

@ -532,203 +532,205 @@ async function getMetaDiff(
export async function metaDiff(req, res) {
const project = await Project.getWithInfo(req.params.projectId);
const base = project.bases[0];
// @ts-ignore
const sqlClient = NcConnectionMgrv2.getSqlClient(base);
const changes = await getMetaDiff(sqlClient, project, base);
const changes = []
for (const base of project.bases) {
// @ts-ignore
const sqlClient = NcConnectionMgrv2.getSqlClient(base);
changes.push(await getMetaDiff(sqlClient, project, base));
}
res.json(changes);
}
export async function metaDiffSync(req, res) {
const project = await Project.getWithInfo(req.params.projectId);
const base = project.bases[0];
const virtualColumnInsert: Array<() => Promise<void>> = [];
// @ts-ignore
const sqlClient = NcConnectionMgrv2.getSqlClient(base);
const changes = await getMetaDiff(sqlClient, project, base);
/* Get all relations */
// const relations = (await sqlClient.relationListAll())?.data?.list;
for (const { table_name, detectedChanges } of changes) {
// reorder changes to apply relation remove changes
// before column remove to avoid foreign key constraint error
detectedChanges.sort((a, b) => {
return (
applyChangesPriorityOrder.indexOf(b.type) -
applyChangesPriorityOrder.indexOf(a.type)
);
});
for (const change of detectedChanges) {
switch (change.type) {
case MetaDiffType.TABLE_NEW:
{
const columns = (
await sqlClient.columnList({ tn: table_name })
)?.data?.list?.map((c) => ({ ...c, column_name: c.cn }));
mapDefaultPrimaryValue(columns);
const model = await Model.insert(project.id, base.id, {
table_name: table_name,
title: getTableNameAlias(table_name, project.prefix, base),
type: ModelTypes.TABLE,
});
for (const column of columns) {
await Column.insert({
uidt: getColumnUiType(base, column),
fk_model_id: model.id,
...column,
title: getColumnNameAlias(column.column_name, base),
for (const base of project.bases) {
const virtualColumnInsert: Array<() => Promise<void>> = [];
// @ts-ignore
const sqlClient = NcConnectionMgrv2.getSqlClient(base);
const changes = await getMetaDiff(sqlClient, project, base);
/* Get all relations */
// const relations = (await sqlClient.relationListAll())?.data?.list;
for (const { table_name, detectedChanges } of changes) {
// reorder changes to apply relation remove changes
// before column remove to avoid foreign key constraint error
detectedChanges.sort((a, b) => {
return (
applyChangesPriorityOrder.indexOf(b.type) -
applyChangesPriorityOrder.indexOf(a.type)
);
});
for (const change of detectedChanges) {
switch (change.type) {
case MetaDiffType.TABLE_NEW:
{
const columns = (
await sqlClient.columnList({ tn: table_name })
)?.data?.list?.map((c) => ({ ...c, column_name: c.cn }));
mapDefaultPrimaryValue(columns);
const model = await Model.insert(project.id, base.id, {
table_name: table_name,
title: getTableNameAlias(table_name, project.prefix, base),
type: ModelTypes.TABLE,
});
for (const column of columns) {
await Column.insert({
uidt: getColumnUiType(base, column),
fk_model_id: model.id,
...column,
title: getColumnNameAlias(column.column_name, base),
});
}
}
}
break;
case MetaDiffType.VIEW_NEW:
{
const columns = (
await sqlClient.columnList({ tn: table_name })
)?.data?.list?.map((c) => ({ ...c, column_name: c.cn }));
mapDefaultPrimaryValue(columns);
const model = await Model.insert(project.id, base.id, {
table_name: table_name,
title: getTableNameAlias(table_name, project.prefix, base),
type: ModelTypes.VIEW,
});
for (const column of columns) {
await Column.insert({
uidt: getColumnUiType(base, column),
fk_model_id: model.id,
...column,
title: getColumnNameAlias(column.column_name, base),
break;
case MetaDiffType.VIEW_NEW:
{
const columns = (
await sqlClient.columnList({ tn: table_name })
)?.data?.list?.map((c) => ({ ...c, column_name: c.cn }));
mapDefaultPrimaryValue(columns);
const model = await Model.insert(project.id, base.id, {
table_name: table_name,
title: getTableNameAlias(table_name, project.prefix, base),
type: ModelTypes.VIEW,
});
for (const column of columns) {
await Column.insert({
uidt: getColumnUiType(base, column),
fk_model_id: model.id,
...column,
title: getColumnNameAlias(column.column_name, base),
});
}
}
}
break;
case MetaDiffType.TABLE_REMOVE:
case MetaDiffType.VIEW_REMOVE:
{
await change.model.delete();
}
break;
case MetaDiffType.TABLE_COLUMN_ADD:
case MetaDiffType.VIEW_COLUMN_ADD:
{
const columns = (
await sqlClient.columnList({ tn: table_name })
)?.data?.list?.map((c) => ({ ...c, column_name: c.cn }));
const column = columns.find((c) => c.cn === change.cn);
column.uidt = getColumnUiType(base, column);
//todo: inflection
column.title = getColumnNameAlias(column.cn, base);
await Column.insert({ fk_model_id: change.id, ...column });
}
// update old
// populateParams.tableNames.push({ tn });
// populateParams.oldMetas[tn] = oldMetas.find(m => m.tn === tn);
break;
case MetaDiffType.TABLE_COLUMN_TYPE_CHANGE:
case MetaDiffType.VIEW_COLUMN_TYPE_CHANGE:
{
const columns = (
await sqlClient.columnList({ tn: table_name })
)?.data?.list?.map((c) => ({ ...c, column_name: c.cn }));
const column = columns.find((c) => c.cn === change.cn);
const metaFact = ModelXcMetaFactory.create(
{ client: base.type },
{}
);
column.uidt = metaFact.getUIDataType(column);
column.title = change.column.title;
await Column.update(change.column.id, column);
}
break;
case MetaDiffType.TABLE_COLUMN_REMOVE:
case MetaDiffType.VIEW_COLUMN_REMOVE:
await change.column.delete();
break;
case MetaDiffType.TABLE_RELATION_REMOVE:
case MetaDiffType.TABLE_VIRTUAL_M2M_REMOVE:
await change.column.delete();
break;
case MetaDiffType.TABLE_RELATION_ADD:
{
virtualColumnInsert.push(async () => {
const parentModel = await Model.getByIdOrName({
project_id: base.project_id,
base_id: base.id,
table_name: change.rtn,
});
const childModel = await Model.getByIdOrName({
project_id: base.project_id,
base_id: base.id,
table_name: change.tn,
});
const parentCol = await parentModel
.getColumns()
.then((cols) => cols.find((c) => c.column_name === change.rcn));
const childCol = await childModel
.getColumns()
.then((cols) => cols.find((c) => c.column_name === change.cn));
await Column.update(childCol.id, {
...childCol,
uidt: UITypes.ForeignKey,
system: true,
});
if (change.relationType === RelationTypes.BELONGS_TO) {
const title = getUniqueColumnAliasName(
childModel.columns,
`${parentModel.title || parentModel.table_name}`
);
await Column.insert<LinkToAnotherRecordColumn>({
uidt: UITypes.LinkToAnotherRecord,
title,
fk_model_id: childModel.id,
fk_related_model_id: parentModel.id,
type: RelationTypes.BELONGS_TO,
fk_parent_column_id: parentCol.id,
fk_child_column_id: childCol.id,
virtual: false,
break;
case MetaDiffType.TABLE_REMOVE:
case MetaDiffType.VIEW_REMOVE:
{
await change.model.delete();
}
break;
case MetaDiffType.TABLE_COLUMN_ADD:
case MetaDiffType.VIEW_COLUMN_ADD:
{
const columns = (
await sqlClient.columnList({ tn: table_name })
)?.data?.list?.map((c) => ({ ...c, column_name: c.cn }));
const column = columns.find((c) => c.cn === change.cn);
column.uidt = getColumnUiType(base, column);
//todo: inflection
column.title = getColumnNameAlias(column.cn, base);
await Column.insert({ fk_model_id: change.id, ...column });
}
// update old
// populateParams.tableNames.push({ tn });
// populateParams.oldMetas[tn] = oldMetas.find(m => m.tn === tn);
break;
case MetaDiffType.TABLE_COLUMN_TYPE_CHANGE:
case MetaDiffType.VIEW_COLUMN_TYPE_CHANGE:
{
const columns = (
await sqlClient.columnList({ tn: table_name })
)?.data?.list?.map((c) => ({ ...c, column_name: c.cn }));
const column = columns.find((c) => c.cn === change.cn);
const metaFact = ModelXcMetaFactory.create(
{ client: base.type },
{}
);
column.uidt = metaFact.getUIDataType(column);
column.title = change.column.title;
await Column.update(change.column.id, column);
}
break;
case MetaDiffType.TABLE_COLUMN_REMOVE:
case MetaDiffType.VIEW_COLUMN_REMOVE:
await change.column.delete();
break;
case MetaDiffType.TABLE_RELATION_REMOVE:
case MetaDiffType.TABLE_VIRTUAL_M2M_REMOVE:
await change.column.delete();
break;
case MetaDiffType.TABLE_RELATION_ADD:
{
virtualColumnInsert.push(async () => {
const parentModel = await Model.getByIdOrName({
project_id: base.project_id,
base_id: base.id,
table_name: change.rtn,
});
} else if (change.relationType === RelationTypes.HAS_MANY) {
const title = getUniqueColumnAliasName(
childModel.columns,
`${childModel.title || childModel.table_name} List`
);
await Column.insert<LinkToAnotherRecordColumn>({
uidt: UITypes.LinkToAnotherRecord,
title,
fk_model_id: parentModel.id,
fk_related_model_id: childModel.id,
type: RelationTypes.HAS_MANY,
fk_parent_column_id: parentCol.id,
fk_child_column_id: childCol.id,
virtual: false,
const childModel = await Model.getByIdOrName({
project_id: base.project_id,
base_id: base.id,
table_name: change.tn,
});
}
});
}
break;
const parentCol = await parentModel
.getColumns()
.then((cols) => cols.find((c) => c.column_name === change.rcn));
const childCol = await childModel
.getColumns()
.then((cols) => cols.find((c) => c.column_name === change.cn));
await Column.update(childCol.id, {
...childCol,
uidt: UITypes.ForeignKey,
system: true,
});
if (change.relationType === RelationTypes.BELONGS_TO) {
const title = getUniqueColumnAliasName(
childModel.columns,
`${parentModel.title || parentModel.table_name}`
);
await Column.insert<LinkToAnotherRecordColumn>({
uidt: UITypes.LinkToAnotherRecord,
title,
fk_model_id: childModel.id,
fk_related_model_id: parentModel.id,
type: RelationTypes.BELONGS_TO,
fk_parent_column_id: parentCol.id,
fk_child_column_id: childCol.id,
virtual: false,
});
} else if (change.relationType === RelationTypes.HAS_MANY) {
const title = getUniqueColumnAliasName(
childModel.columns,
`${childModel.title || childModel.table_name} List`
);
await Column.insert<LinkToAnotherRecordColumn>({
uidt: UITypes.LinkToAnotherRecord,
title,
fk_model_id: parentModel.id,
fk_related_model_id: childModel.id,
type: RelationTypes.HAS_MANY,
fk_parent_column_id: parentCol.id,
fk_child_column_id: childCol.id,
virtual: false,
});
}
});
}
break;
}
}
}
await NcHelp.executeOperations(virtualColumnInsert, base.type);
// populate m2m relations
await extractAndGenerateManyToManyRelations(await base.getModels());
}
await NcHelp.executeOperations(virtualColumnInsert, base.type);
// populate m2m relations
await extractAndGenerateManyToManyRelations(await base.getModels());
Tele.emit('evt', { evt_type: 'metaDiff:synced' });
res.json({ msg: 'success' });

6
packages/nocodb/src/lib/meta/api/modelVisibilityApis.ts

@ -3,7 +3,6 @@ import ModelRoleVisibility from '../../models/ModelRoleVisibility';
import { Router } from 'express';
import { Tele } from 'nc-help';
import ncMetaAclMw from '../helpers/ncMetaAclMw';
import Project from '../../models/Project';
import { metaApiMetrics } from '../helpers/apiMetrics';
async function xcVisibilityMetaSetAll(req, res) {
Tele.emit('evt', { evt_type: 'uiAcl:updated' });
@ -49,13 +48,12 @@ export async function xcVisibilityMetaGet(
const roles = ['owner', 'creator', 'viewer', 'editor', 'commenter', 'guest'];
const defaultDisabled = roles.reduce((o, r) => ({ ...o, [r]: false }), {});
const project = await Project.getWithInfo(projectId);
let models =
_models ||
(await Model.list({
project_id: projectId,
base_id: project?.bases?.[0]?.id,
base_id: undefined,
}));
models = includeM2M ? models : (models.filter((t) => !t.mm) as Model[]);

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

@ -452,15 +452,12 @@ async function populateMeta(base: Base, project: Project): Promise<any> {
return info;
}
export async function projectInfoGet(req, res) {
const project = await Project.getWithInfo(req.params.projectId);
export async function projectInfoGet(_req, res) {
res.json({
Node: process.version,
Arch: process.arch,
Platform: process.platform,
Docker: isDocker(),
Database: project.bases?.[0]?.type,
ProjectOnRootDB: !!project?.is_meta,
RootDB: Noco.getConfig()?.meta?.db?.client,
PackageVersion: packageVersion,
});
@ -469,22 +466,25 @@ export async function projectInfoGet(req, res) {
export async function projectCost(req, res) {
let cost = 0;
const project = await Project.getWithInfo(req.params.projectId);
const sqlClient = NcConnectionMgrv2.getSqlClient(project.bases[0]);
const userCount = await ProjectUser.getUsersCount(req.query);
const recordCount = (await sqlClient.totalRecords())?.data.TotalRecords;
if (recordCount > 100000) {
// 36,000 or $79/user/month
cost = Math.max(36000, 948 * userCount);
} else if (recordCount > 50000) {
// $36,000 or $50/user/month
cost = Math.max(36000, 600 * userCount);
} else if (recordCount > 10000) {
// $240/user/yr
cost = Math.min(240 * userCount, 36000);
} else if (recordCount > 1000) {
// $120/user/yr
cost = Math.min(120 * userCount, 36000);
for (const base of project.bases) {
const sqlClient = NcConnectionMgrv2.getSqlClient(base);
const userCount = await ProjectUser.getUsersCount(req.query);
const recordCount = (await sqlClient.totalRecords())?.data.TotalRecords;
if (recordCount > 100000) {
// 36,000 or $79/user/month
cost = Math.max(36000, 948 * userCount);
} else if (recordCount > 50000) {
// $36,000 or $50/user/month
cost = Math.max(36000, 600 * userCount);
} else if (recordCount > 10000) {
// $240/user/yr
cost = Math.min(240 * userCount, 36000);
} else if (recordCount > 1000) {
// $120/user/yr
cost = Math.min(120 * userCount, 36000);
}
}
Tele.event({

62
packages/nocodb/src/lib/models/Model.ts

@ -597,7 +597,7 @@ export default class Model implements TableType {
aliasOrId,
}: {
project_id: string;
base_id: string | undefined;
base_id?: string;
aliasOrId: string;
},
ncMeta = Noco.ncMeta
@ -610,27 +610,49 @@ export default class Model implements TableType {
CacheGetType.TYPE_OBJECT
));
if (!modelId) {
const model = await ncMeta.metaGet2(
null,
null,
MetaTable.MODELS,
{ project_id, base_id },
null,
{
_or: [
const model = base_id
? await ncMeta.metaGet2(
null,
null,
MetaTable.MODELS,
{ project_id, base_id },
null,
{
id: {
eq: aliasOrId,
},
},
_or: [
{
id: {
eq: aliasOrId,
},
},
{
title: {
eq: aliasOrId,
},
},
],
}
)
: await ncMeta.metaGet2(
null,
null,
MetaTable.MODELS,
{ project_id },
null,
{
title: {
eq: aliasOrId,
},
},
],
}
);
_or: [
{
id: {
eq: aliasOrId,
},
},
{
title: {
eq: aliasOrId,
},
},
],
}
);
if (model) {
await NocoCache.set(
`${CacheScope.MODEL}:${project_id}:${aliasOrId}`,

Loading…
Cancel
Save