From 5b90eb862f6dbe7a09ab78478a6763b2a0543a7d Mon Sep 17 00:00:00 2001 From: abearxiong Date: Sat, 13 Dec 2025 23:20:26 +0800 Subject: [PATCH] test: migration --- auto/migration /index.ts | 122 +++++++++++++++++++++++++++++++++++++++ test/auto-common.ts | 16 +++++ test/migration.ts | 35 +++++++++++ 3 files changed, 173 insertions(+) create mode 100644 auto/migration /index.ts create mode 100644 test/auto-common.ts create mode 100644 test/migration.ts diff --git a/auto/migration /index.ts b/auto/migration /index.ts new file mode 100644 index 0000000..d3b2656 --- /dev/null +++ b/auto/migration /index.ts @@ -0,0 +1,122 @@ +import { NocoApi } from "../../src/index.ts"; + +type TableOptions = { + fromTableId: string; + fromNoco?: NocoApi; + toNoco?: NocoApi; + toBaseId?: string; +} +export class TableMigration { + fromTableId: string; + fromNoco: NocoApi; + toNoco: NocoApi; + toBaseId: string; + constructor(options: TableOptions) { + this.fromTableId = options.fromTableId; + this.fromNoco = options.fromNoco!; + this.toNoco = options.toNoco!; + this.toBaseId = options.toBaseId!; + } + async migrate() { + const res = await this.migrateTable(); + console.log('migrate table result:', res); + this.toNoco.record.table = res.id; + this.fromNoco.record.table = this.fromTableId; + const fromTable = await this.fromNoco?.record.list({ limit: 2000 }); + const list = fromTable?.data.list || []; + let _list = list.map(item => { + delete item['CreatedAt']; + delete item['UpdatedAt']; + return item; + }); + console.log('_list', _list.length); + const newRecord = await this.toNoco.record.create(_list); + console.log('migrate success:', this.fromTableId); + } + async hasTable(title: string): Promise<{ id: string, exists?: boolean }> { + const res = await this.toNoco?.meta.tables.list(this.toBaseId); + const tables = res?.data?.list || []; + for (const table of tables) { + if (table.title === title) { + return { id: table.id, exists: true }; + } + } + return { id: '' }; + } + async migrateTable() { + const res = await this.fromNoco?.meta.tables.getTableMeta(this.fromTableId); + const columns = res?.data.columns; + const title = res?.data.title; + const hasTableRes = await this.hasTable(title || ''); + if (hasTableRes.id) { + return hasTableRes; + } + const clearCoulmns = ['nc_order', 'nc_created_by', 'nc_created_at', 'nc_updated_by', 'nc_updated_at', 'nc_owner_role', 'nc_owner_user', 'nc_status', 'CreatedAt', 'UpdatedAt']; + const _newColumns = []; + for (const col of columns || []) { + const createColData = { + title: col.title, + uidt: col.uidt, + description: col.description, + pk: col.pk, + pv: col.pv, + rqd: col.rqd, + cdf: col.cdf, + colOptions: col.colOptions, + }; + if (clearCoulmns.includes(col.title)) { + continue; + } + _newColumns.push(createColData); + } + const createRes = await this.toNoco.meta.tables.createTable(this.toBaseId, { + title: res?.data.title || 'Migrated Table', + description: res?.data.description || '', + columns: _newColumns, + }); + return { id: createRes.data.id }; + } +} + +export class BaseMigration { + fromBaseId: string; + toNoco: NocoApi; + toBaseId: string; + fromNoco: NocoApi; + constructor(options: { toNoco: NocoApi; toBaseId: string, fromNoco: NocoApi; fromBaseId: string }) { + this.toNoco = options.toNoco; + this.toBaseId = options.toBaseId; + this.fromNoco = options.fromNoco; + this.fromBaseId = options.fromBaseId; + } + async getTableList() { + const res = await this.fromNoco?.meta.tables.list(this.fromBaseId); + return res?.data?.list || []; + } + async migrate(options?: MigrateOptions) { + const tableList = await this.getTableList(); + const excludeTables = options?.excludeTables || []; + const includeTables = options?.includeTables || []; + for (const table of tableList) { + if (excludeTables.includes(table.title || '')) { + console.log('skip table:', table.title); + continue; + } + if (includeTables.length > 0 && !includeTables.includes(table.title || '')) { + console.log('skip table not in include list:', table.title); + continue; + } + const tableMigration = new TableMigration({ + fromTableId: table.id, + fromNoco: this.fromNoco, + toNoco: this.toNoco, + toBaseId: this.toBaseId, + }); + await tableMigration.migrate(); + } + } +} +type MigrateOptions = { + excludeTables?: string[]; + includeTables?: string[]; +} \ No newline at end of file diff --git a/test/auto-common.ts b/test/auto-common.ts new file mode 100644 index 0000000..505108e --- /dev/null +++ b/test/auto-common.ts @@ -0,0 +1,16 @@ +import { NocoApi, } from './../src/main.ts'; +import { useConfig } from '@kevisual/use-config' +export const config = useConfig() +import util from 'node:util'; + +export const nocoApi = new NocoApi({ + baseURL: config.NOCODB_URL || 'http://localhost:8080', + token: config.NOCODB_API_KEY || '', +}); +export const toNocoApi = new NocoApi({ + baseURL: config.TO_NOCODB_URL || 'http://localhost:8080', + token: config.TO_NOCODB_API_KEY || '', +}); +export const showMore = (obj: any) => { + return util.inspect(obj, { depth: null, colors: true }); +} \ No newline at end of file diff --git a/test/migration.ts b/test/migration.ts new file mode 100644 index 0000000..704804c --- /dev/null +++ b/test/migration.ts @@ -0,0 +1,35 @@ +import { TableMigration, BaseMigration } from "../auto/migration "; + +import { nocoApi, toNocoApi } from "./auto-common"; + +const toBaseId = 'pjc9ks10ttrv6vz'; + +const baseId = 'p0np8adsoc79odk'; + +const tableId = 'mgt91mysuwts4e6'; + +async function migrateTable() { + const tableMigration = new TableMigration({ + fromTableId: tableId, + fromNoco: nocoApi, + toNoco: toNocoApi, + toBaseId, + }); + await tableMigration.migrate(); +} + +// await migrateTable(); + +async function mirgtateBase() { + const baseMigration = new BaseMigration({ + fromBaseId: baseId, + toBaseId, + fromNoco: nocoApi, + toNoco: toNocoApi, + }); + await baseMigration.migrate({ + excludeTables: ['控制中枢', '人生备忘录'], + // includeTables: ['人生备忘录'] + }); +} +mirgtateBase(); \ No newline at end of file