mirror of https://github.com/nocodb/nocodb
Wing-Kam Wong
2 years ago
27 changed files with 785 additions and 581 deletions
@ -0,0 +1,34 @@ |
|||||||
|
name: cleanup caches by branch |
||||||
|
on: |
||||||
|
pull_request: |
||||||
|
types: |
||||||
|
- closed |
||||||
|
jobs: |
||||||
|
cleanup: |
||||||
|
runs-on: ubuntu-latest |
||||||
|
steps: |
||||||
|
- name: Check out code |
||||||
|
uses: actions/checkout@v3 |
||||||
|
|
||||||
|
- name: Cleanup |
||||||
|
run: | |
||||||
|
gh extension install actions/gh-actions-cache |
||||||
|
|
||||||
|
REPO=${{ github.repository }} |
||||||
|
|
||||||
|
# get the branch |
||||||
|
BRANCH="refs/pull/${{ github.event.pull_request.number }}/merge" |
||||||
|
|
||||||
|
# fetch list of cache key |
||||||
|
cacheKeysForPR=$(gh actions-cache list -R $REPO -B $BRANCH | cut -f 1 ) |
||||||
|
|
||||||
|
# set this to not fail the workflow while deleting cache keys |
||||||
|
set +e |
||||||
|
|
||||||
|
# delete cache key |
||||||
|
for cacheKey in $cacheKeysForPR |
||||||
|
do |
||||||
|
gh actions-cache delete $cacheKey -R $REPO -B $BRANCH --confirm |
||||||
|
done |
||||||
|
env: |
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} |
@ -0,0 +1,3 @@ |
|||||||
|
import { populateMeta } from './populateMeta'; |
||||||
|
|
||||||
|
export { populateMeta }; |
@ -0,0 +1,278 @@ |
|||||||
|
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 View from '../../../models/View'; |
||||||
|
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, ViewTypes } 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 = 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.cstn, |
||||||
|
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); |
||||||
|
|
||||||
|
// fix pv column for created grid views
|
||||||
|
const models = await Model.list({ project_id: project.id, base_id: base.id }); |
||||||
|
|
||||||
|
for (const model of models) { |
||||||
|
const views = await model.getViews(); |
||||||
|
for (const view of views) { |
||||||
|
if (view.type === ViewTypes.GRID) { |
||||||
|
await View.fixPVColumnForView(view.id); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
const t1 = process.hrtime(t); |
||||||
|
const t2 = t1[0] + t1[1] / 1000000000; |
||||||
|
|
||||||
|
(info as any).timeTaken = t2.toFixed(1); |
||||||
|
|
||||||
|
return info; |
||||||
|
} |
@ -0,0 +1,90 @@ |
|||||||
|
import { NcUpgraderCtx } from './NcUpgrader'; |
||||||
|
import { MetaTable } from '../utils/globals'; |
||||||
|
|
||||||
|
// before 0.104.3, primary value column can be in any position in table
|
||||||
|
// with this upgrade we introduced sticky primary column feature
|
||||||
|
// this upgrader will make primary value column first column in grid views
|
||||||
|
|
||||||
|
export default async function ({ ncMeta }: NcUpgraderCtx) { |
||||||
|
const grid_columns = await ncMeta.metaList2( |
||||||
|
null, |
||||||
|
null, |
||||||
|
MetaTable.GRID_VIEW_COLUMNS |
||||||
|
); |
||||||
|
const grid_views = [...new Set(grid_columns.map((col) => col.fk_view_id))]; |
||||||
|
|
||||||
|
for (const view_id of grid_views) { |
||||||
|
// get a list of view columns sorted by order
|
||||||
|
const view_columns = await ncMeta.metaList2( |
||||||
|
null, |
||||||
|
null, |
||||||
|
MetaTable.GRID_VIEW_COLUMNS, |
||||||
|
{ |
||||||
|
condition: { |
||||||
|
fk_view_id: view_id, |
||||||
|
}, |
||||||
|
orderBy: { |
||||||
|
order: 'asc', |
||||||
|
}, |
||||||
|
} |
||||||
|
); |
||||||
|
const view_columns_meta = []; |
||||||
|
|
||||||
|
// get column meta for each view column
|
||||||
|
for (const col of view_columns) { |
||||||
|
const col_meta = await ncMeta.metaGet(null, null, MetaTable.COLUMNS, { |
||||||
|
id: col.fk_column_id, |
||||||
|
}); |
||||||
|
view_columns_meta.push(col_meta); |
||||||
|
} |
||||||
|
|
||||||
|
const primary_value_column_meta = view_columns_meta.find((col) => col.pv); |
||||||
|
|
||||||
|
if (primary_value_column_meta) { |
||||||
|
const primary_value_column = view_columns.find( |
||||||
|
(col) => col.fk_column_id === primary_value_column_meta.id |
||||||
|
); |
||||||
|
const primary_value_column_index = view_columns.findIndex( |
||||||
|
(col) => col.fk_column_id === primary_value_column_meta.id |
||||||
|
); |
||||||
|
const view_orders = view_columns.map((col) => col.order); |
||||||
|
const view_min_order = Math.min(...view_orders); |
||||||
|
|
||||||
|
// if primary_value_column is not visible, make it visible
|
||||||
|
if (!primary_value_column.show) { |
||||||
|
await ncMeta.metaUpdate( |
||||||
|
null, |
||||||
|
null, |
||||||
|
MetaTable.GRID_VIEW_COLUMNS, |
||||||
|
{ show: true }, |
||||||
|
primary_value_column.id |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
if ( |
||||||
|
primary_value_column.order === view_min_order && |
||||||
|
view_orders.filter((o) => o === view_min_order).length === 1 |
||||||
|
) { |
||||||
|
// if primary_value_column is in first order do nothing
|
||||||
|
continue; |
||||||
|
} else { |
||||||
|
// if primary_value_column not in first order, move it to the start of array
|
||||||
|
if (primary_value_column_index !== 0) { |
||||||
|
const temp_pv = view_columns.splice(primary_value_column_index, 1); |
||||||
|
view_columns.unshift(...temp_pv); |
||||||
|
} |
||||||
|
|
||||||
|
// update order of all columns in view to match the order in array
|
||||||
|
for (let i = 0; i < view_columns.length; i++) { |
||||||
|
await ncMeta.metaUpdate( |
||||||
|
null, |
||||||
|
null, |
||||||
|
MetaTable.GRID_VIEW_COLUMNS, |
||||||
|
{ order: i + 1 }, |
||||||
|
view_columns[i].id |
||||||
|
); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue