Browse Source

fix: add updgrader to recover broken links

pull/9684/head
Pranav C 2 months ago
parent
commit
49216a1128
  1. 2
      packages/nocodb/src/providers/init-meta-service.provider.ts
  2. 2
      packages/nocodb/src/version-upgrader/NcUpgrader.ts
  3. 348
      packages/nocodb/src/version-upgrader/upgraders/0227002_ncBrokenLinkRecovery.ts

2
packages/nocodb/src/providers/init-meta-service.provider.ts

@ -31,7 +31,7 @@ export const InitMetaServiceProvider: FactoryProvider = {
const config = await NcConfig.createByEnv();
// set version
process.env.NC_VERSION = '0225002';
process.env.NC_VERSION = '0257002';
// set migration jobs version
process.env.NC_MIGRATION_JOBS_VERSION = '2';

2
packages/nocodb/src/version-upgrader/NcUpgrader.ts

@ -12,6 +12,7 @@ import ncXcdbLTARUpgrader from './upgraders/0108002_ncXcdbLTARUpgrader';
import ncXcdbLTARIndexUpgrader from './upgraders/0111002_ncXcdbLTARIndexUpgrader';
import ncXcdbCreatedAndUpdatedSystemFieldsUpgrader from './upgraders/0111005_ncXcdbCreatedAndUpdatedSystemFieldsUpgrader';
import ncDatasourceDecrypt from './upgraders/0225002_ncDatasourceDecrypt';
import ncBrokenLinkRecovery from './upgraders/0227002_ncBrokenLinkRecovery';
// import dupColMergeUpgrader from './0205003_dupColMergeUpgrader';
import type { MetaService } from '~/meta/meta.service';
import type { NcConfig } from '~/interface/config';
@ -151,6 +152,7 @@ export default class NcUpgrader {
{ name: '0111002', handler: ncXcdbLTARIndexUpgrader },
{ name: '0111005', handler: ncXcdbCreatedAndUpdatedSystemFieldsUpgrader },
{ name: '0225002', handler: ncDatasourceDecrypt },
{ name: '0257002', handler: ncBrokenLinkRecovery },
// disable for now
// { name: '0227000', handler: dupColMergeUpgrader },
];

348
packages/nocodb/src/version-upgrader/upgraders/0227002_ncBrokenLinkRecovery.ts

@ -1,7 +1,8 @@
import { RelationTypes, UITypes } from 'nocodb-sdk';
import type { NcUpgraderCtx } from '~/version-upgrader/NcUpgrader';
import type { MetaService } from '~/meta/meta.service';
import { MetaTable } from '~/utils/globals';
import {MetaService} from "~/meta/meta.service";
import { Column } from '~/models';
/**
* This upgrader look for any broken link and try to recover it
@ -25,7 +26,7 @@ export default async function ({ ncMeta }: NcUpgraderCtx) {
.leftJoin(
MetaTable.COL_RELATIONS,
`${MetaTable.COLUMNS}.id`,
`${MetaTable.COL_SELECT_OPTIONS}.fk_column_id`,
`${MetaTable.COL_RELATIONS}.fk_column_id`,
)
.where(`${MetaTable.COLUMNS}.uidt`, UITypes.LinkToAnotherRecord)
.whereNull(`${MetaTable.COL_RELATIONS}.id`);
@ -78,7 +79,10 @@ export default async function ({ ncMeta }: NcUpgraderCtx) {
const linksQb = ncMeta
.knex(MetaTable.COL_RELATIONS)
.select(`${MetaTable.COL_RELATIONS}.*`)
.where(`${MetaTable.COL_RELATIONS}.fk_related_model_id`, column.fk_model_id);
.where(
`${MetaTable.COL_RELATIONS}.fk_related_model_id`,
column.fk_model_id,
);
if (relatedTableId) {
linksQb
.join(
@ -87,163 +91,197 @@ export default async function ({ ncMeta }: NcUpgraderCtx) {
`${MetaTable.COLUMNS}.id`,
)
.where(`${MetaTable.COLUMNS}.fk_model_id`, relatedTableId);
}
const links = await linksQb;
// iterate over all links which is related to current table and if found relation which doesn't have link in the related table then use it to populate colOptions
for (const link of links) {
let columnInCurrTable = null;
if (link.type === RelationTypes.HAS_MANY) {
// check for bt column in current table
columnInCurrTable = await ncMeta
.knex(MetaTable.COL_RELATIONS)
.join(
MetaTable.COLUMNS,
`${MetaTable.COL_RELATIONS}.fk_column_id`,
`${MetaTable.COLUMNS}.id`,
)
.where(`${MetaTable.COL_RELATIONS}.fk_model_id`, currentTableId)
.where(
`${MetaTable.COL_RELATIONS}.fk_related_model_id`,
relatedTableId,
)
.where(`${MetaTable.COL_RELATIONS}.type`, RelationTypes.BELONGS_TO)
.where(
`${MetaTable.COL_RELATIONS}.fk_child_column_id`,
link.fk_child_column_id,
)
.where(
`${MetaTable.COL_RELATIONS}.fk_parent_column_id`,
link.fk_parent_column_id,
);
} else if (link.type === RelationTypes.ONE_TO_ONE) {
// check for one to one column in current table and confirm type in meta
columnInCurrTable = await ncMeta
.knex(MetaTable.COL_RELATIONS)
.join(
MetaTable.COLUMNS,
`${MetaTable.COL_RELATIONS}.fk_column_id`,
`${MetaTable.COLUMNS}.id`,
)
.where(`${MetaTable.COL_RELATIONS}.fk_model_id`, currentTableId)
.where(
`${MetaTable.COL_RELATIONS}.fk_related_model_id`,
relatedTableId,
)
.where(`${MetaTable.COL_RELATIONS}.type`, RelationTypes.ONE_TO_ONE)
.where(
`${MetaTable.COL_RELATIONS}.fk_child_column_id`,
link.fk_child_column_id,
)
.where(
`${MetaTable.COL_RELATIONS}.fk_parent_column_id`,
link.fk_parent_column_id,
);
} else if (link.type === RelationTypes.BELONGS_TO) {
// check for hm column in current table
columnInCurrTable = await ncMeta
.knex(MetaTable.COL_RELATIONS)
.join(
MetaTable.COLUMNS,
`${MetaTable.COL_RELATIONS}.fk_column_id`,
`${MetaTable.COLUMNS}.id`,
)
.where(`${MetaTable.COL_RELATIONS}.fk_model_id`, currentTableId)
.where(
`${MetaTable.COL_RELATIONS}.fk_related_model_id`,
relatedTableId,
)
.where(`${MetaTable.COL_RELATIONS}.type`, RelationTypes.HAS_MANY)
.where(
`${MetaTable.COL_RELATIONS}.fk_child_column_id`,
link.fk_child_column_id,
)
.where(
`${MetaTable.COL_RELATIONS}.fk_parent_column_id`,
link.fk_parent_column_id,
);
} else if (link.type === RelationTypes.MANY_TO_MANY) {
// check for mtm column in current table
columnInCurrTable = await ncMeta
.knex(MetaTable.COL_RELATIONS)
.join(
MetaTable.COLUMNS,
`${MetaTable.COL_RELATIONS}.fk_column_id`,
`${MetaTable.COLUMNS}.id`,
)
.where(`${MetaTable.COL_RELATIONS}.fk_model_id`, currentTableId)
.where(
`${MetaTable.COL_RELATIONS}.fk_related_model_id`,
relatedTableId,
)
.where(`${MetaTable.COL_RELATIONS}.type`, RelationTypes.BELONGS_TO)
.where(
`${MetaTable.COL_RELATIONS}.fk_child_column_id`,
link.fk_parent_column_id,
)
.where(
`${MetaTable.COL_RELATIONS}.fk_parent_column_id`,
link.fk_parent_column_id,
)
.where(
`${MetaTable.COL_RELATIONS}.fk_mm_model_id`,
link.fk_mm_model_id,
)
.where(
`${MetaTable.COL_RELATIONS}.fk_mm_child_column_id`,
link.fk_mm_parent_column_id,
)
.where(
`${MetaTable.COL_RELATIONS}.fk_mm_parent_column_id`,
link.fk_mm_child_column_id,
);
}
const links = await linksQb;
// iterate over all links which is related to current table and if found relation which doesn't have link in the related table then use it to populate colOptions
for (const link of links) {
let columnInCurrTable null;
if (link.type === RelationTypes.HAS_MANY) {
// check for bt column in current table
columnInCurrTable = await ncMeta
.knex(MetaTable.COL_RELATIONS)
.join(
MetaTable.COLUMNS,
`${MetaTable.COL_RELATIONS}.fk_column_id`,
`${MetaTable.COLUMNS}.id`,
)
.where(`${MetaTable.COL_RELATIONS}.fk_model_id`, currentTableId)
.where(
`${MetaTable.COL_RELATIONS}.fk_related_model_id`,
relatedTableId,
)
.where(`${MetaTable.COL_RELATIONS}.type`, RelationTypes.BELONGS_TO)
.where(
`${MetaTable.COL_RELATIONS}.fk_child_column_id`,
link.fk_child_column_id,
)
.where(
`${MetaTable.COL_RELATIONS}.fk_parent_column_id`,
link.fk_parent_column_id,
);
} else if (link.type === RelationTypes.ONE_TO_ONE) {
// check for one to one column in current table and confirm type in meta
columnInCurrTable = await ncMeta
.knex(MetaTable.COL_RELATIONS)
.join(
MetaTable.COLUMNS,
`${MetaTable.COL_RELATIONS}.fk_column_id`,
`${MetaTable.COLUMNS}.id`,
)
.where(`${MetaTable.COL_RELATIONS}.fk_model_id`, currentTableId)
.where(
`${MetaTable.COL_RELATIONS}.fk_related_model_id`,
relatedTableId,
)
.where(`${MetaTable.COL_RELATIONS}.type`, RelationTypes.ONE_TO_ONE)
.where(
`${MetaTable.COL_RELATIONS}.fk_child_column_id`,
link.fk_child_column_id,
)
.where(
`${MetaTable.COL_RELATIONS}.fk_parent_column_id`,
link.fk_parent_column_id,
);
} else if (link.type === RelationTypes.BELONGS_TO) {
// check for hm column in current table
columnInCurrTable = await ncMeta
.knex(MetaTable.COL_RELATIONS)
.join(
MetaTable.COLUMNS,
`${MetaTable.COL_RELATIONS}.fk_column_id`,
`${MetaTable.COLUMNS}.id`,
)
.where(`${MetaTable.COL_RELATIONS}.fk_model_id`, currentTableId)
.where(
`${MetaTable.COL_RELATIONS}.fk_related_model_id`,
relatedTableId,
)
.where(`${MetaTable.COL_RELATIONS}.type`, RelationTypes.HAS_MANY)
.where(
`${MetaTable.COL_RELATIONS}.fk_child_column_id`,
link.fk_child_column_id,
)
.where(
`${MetaTable.COL_RELATIONS}.fk_parent_column_id`,
link.fk_parent_column_id,
);
} else if (link.type === RelationTypes.MANY_TO_MANY) {
// check for mtm column in current table
columnInCurrTable = await ncMeta
.knex(MetaTable.COL_RELATIONS)
.join(
MetaTable.COLUMNS,
`${MetaTable.COL_RELATIONS}.fk_column_id`,
`${MetaTable.COLUMNS}.id`,
)
.where(`${MetaTable.COL_RELATIONS}.fk_model_id`, currentTableId)
.where(
`${MetaTable.COL_RELATIONS}.fk_related_model_id`,
relatedTableId,
)
.where(`${MetaTable.COL_RELATIONS}.type`, RelationTypes.BELONGS_TO)
.where(
`${MetaTable.COL_RELATIONS}.fk_child_column_id`,
link.fk_parent_column_id,
)
.where(
`${MetaTable.COL_RELATIONS}.fk_parent_column_id`,
link.fk_parent_column_id,
)
.where(
`${MetaTable.COL_RELATIONS}.fk_mm_model_id`,
link.fk_mm_model_id,
)
.where(
`${MetaTable.COL_RELATIONS}.fk_mm_child_column_id`,
link.fk_mm_parent_column_id,
)
.where(
`${MetaTable.COL_RELATIONS}.fk_mm_parent_column_id`,
link.fk_mm_child_column_id,
);
if (!columnInCurrTable) {
// generate meta and insert into colOptions
const commonProps = {
id: (ncMeta as MetaService).genNanoid(MetaTable.COL_RELATIONS),
fk_column_id: column.id,
fk_related_model_id: relatedTableId,
created_at: link.created_at,
updated_at: link.updated_at,
virtual: link.virtual,
};
// based on type insert data into colOptions
switch (link.type) {
case RelationTypes.HAS_MANY:
// insert data into colOptions
ncMeta.knex(MetaTable.COL_RELATIONS).insert({
...commonProps,
type: RelationTypes.BELONGS_TO,
fk_child_column_id: link.fk_child_column_id,
fk_parent_column_id: link.fk_parent_column_id,
});
break;
case RelationTypes.ONE_TO_ONE:
// todo:
const meta = {
bt: false,
};
// insert data into colOptions
ncMeta.knex(MetaTable.COL_RELATIONS).insert({
...commonProps,
type: RelationTypes.ONE_TO_ONE,
fk_child_column_id: link.fk_child_column_id,
fk_parent_column_id: link.fk_parent_column_id,
meta,
});
break;
case RelationTypes.BELONGS_TO:
// insert data into colOptions
ncMeta.knex(MetaTable.COL_RELATIONS).insert({
...commonProps,
type: RelationTypes.HAS_MANY,
fk_child_column_id: link.fk_child_column_id,
fk_parent_column_id: link.fk_parent_column_id,
});
break;
case RelationTypes.MANY_TO_MANY:
// insert data into colOptions
ncMeta.knex(MetaTable.COL_RELATIONS).insert({
...commonProps,
type: RelationTypes.ONE_TO_ONE,
fk_child_column_id: link.fk_parent_column_id,
fk_parent_column_id: link.fk_child_column_id,
fk_mm_model_id: link.fk_mm_model_id,
fk_mm_child_column_id: link.fk_mm_parent_column_id,
fk_mm_parent_column_id: link.fk_mm_child_column_id,
});
break;
}
if(!columnInCurrTable) {
// generate meta and insert into colOptions
// based on type insert data into colOptions
switch (link.type) {
case RelationTypes.HAS_MANY:
// insert data into colOptions
ncMeta.knex(MetaTable.COL_RELATIONS).insert({
id: (ncMeta as MetaService).genNanoid(MetaTable.COL_RELATIONS),
type: RelationTypes.BELONGS_TO,
fk_column_id: column.id,
fk_related_model_id: relatedTableId,
break;
case RelationTypes.ONE_TO_ONE:
// insert data into colOptions
break;
case RelationTypes.BELONGS_TO:
// insert data into colOptions
break;
case RelationTypes.MANY_TO_MANY:
// insert data into colOptions
break;
}
break;
}
break;
} else {
logger.error(
`Couldn't find any column in current table which is related to the link '${link.id}'.`,
);
// delete the link column since it's not useful anymore and not recoverable
await Column.delete(
{
workspace_id: column.workspace_id,
base_id: column.base_id,
},
column.id,
ncMeta,
);
}
}
}

Loading…
Cancel
Save