Skip to content

Commit f7c90e7

Browse files
committed
feat: support move table
1 parent 44ff3bc commit f7c90e7

File tree

20 files changed

+1546
-3
lines changed

20 files changed

+1546
-3
lines changed

‎apps/nestjs-backend/src/db-provider/db.provider.interface.ts‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import type { IFilterQueryInterface } from './filter-query/filter-query.interfac
1313
import type { IGroupQueryExtra, IGroupQueryInterface } from './group-query/group-query.interface';
1414
import type { IndexBuilderAbstract } from './index-query/index-abstract-builder';
1515
import type { IntegrityQueryAbstract } from './integrity-query/abstract';
16+
import type { MoveTableQueryAbstract } from './move-table-query/abstract';
1617
import type { ISortQueryInterface } from './sort-query/sort-query.interface';
1718

1819
export type IFilterQueryExtra = {
@@ -62,6 +63,8 @@ export interface IDbProvider {
6263
value: string
6364
): string;
6465

66+
moveTableQuery(queryBuilder: Knex.QueryBuilder): MoveTableQueryAbstract;
67+
6568
updateJsonArrayColumn(
6669
tableName: string,
6770
columnName: string,
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import type { Knex } from 'knex';
2+
3+
export abstract class MoveTableQueryAbstract {
4+
constructor(protected readonly queryBuilder: Knex.QueryBuilder) {}
5+
6+
abstract getSourceBaseJunctionTableName(sourceBaseId: string): Knex.QueryBuilder;
7+
8+
abstract getFullSourceBaseJunctionTableNames(
9+
sourceBaseId: string,
10+
nameFromSqlQuery: string[]
11+
): string[];
12+
13+
abstract getMovedDbTableName(dbTableName: string, targetSchema: string): string;
14+
15+
abstract updateTableSchema(sourceDbTableName: string, targetSchema: string): Knex.QueryBuilder;
16+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import type { Knex } from 'knex';
2+
import { MoveTableQueryAbstract } from './abstract';
3+
4+
export class MoveTableQueryPostgres extends MoveTableQueryAbstract {
5+
protected knex: Knex.Client;
6+
constructor(queryBuilder: Knex.QueryBuilder) {
7+
super(queryBuilder);
8+
this.knex = queryBuilder.client;
9+
}
10+
11+
getSourceBaseJunctionTableName(sourceBaseId: string): Knex.QueryBuilder {
12+
return this.queryBuilder
13+
.select('table_name')
14+
.from('information_schema.tables')
15+
.where('table_schema', sourceBaseId)
16+
.where('table_name', 'like', '%junction_%')
17+
.where('table_type', 'BASE TABLE');
18+
}
19+
20+
getFullSourceBaseJunctionTableNames(sourceBaseId: string, nameFromSqlQuery: string[]): string[] {
21+
if (!Array.isArray(nameFromSqlQuery)) {
22+
return [];
23+
}
24+
return nameFromSqlQuery.map((name) => {
25+
return `${sourceBaseId}.${name}`;
26+
});
27+
}
28+
29+
getMovedDbTableName(dbTableName: string, targetSchema: string): string {
30+
const [, tableName] = dbTableName.split('.');
31+
return `${targetSchema}.${tableName}`;
32+
}
33+
34+
updateTableSchema(sourceDbTableName: string, targetSchema: string): Knex.QueryBuilder {
35+
const [schema, tableName] = sourceDbTableName.split('.');
36+
return this.knex.raw(
37+
`
38+
ALTER TABLE ??.??
39+
SET SCHEMA ??
40+
`,
41+
[schema, tableName, targetSchema]
42+
);
43+
}
44+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import type { Knex } from 'knex';
2+
import { MoveTableQueryAbstract } from './abstract';
3+
4+
export class MoveTableQuerySqlite extends MoveTableQueryAbstract {
5+
protected knex: Knex.Client;
6+
constructor(queryBuilder: Knex.QueryBuilder) {
7+
super(queryBuilder);
8+
this.knex = queryBuilder.client;
9+
}
10+
11+
getSourceBaseJunctionTableName(sourceBaseId: string): Knex.QueryBuilder {
12+
return this.queryBuilder
13+
.select('name as table_name')
14+
.from('sqlite_master')
15+
.where('type', 'table')
16+
.where('name', 'like', `%${sourceBaseId}_junction%`);
17+
}
18+
19+
getFullSourceBaseJunctionTableNames(sourceBaseId: string, nameFromSqlQuery: string[]) {
20+
if (!Array.isArray(nameFromSqlQuery)) {
21+
return [];
22+
}
23+
return nameFromSqlQuery;
24+
}
25+
26+
getMovedDbTableName(dbTableName: string, targetSchema: string): string {
27+
const schemaDelimiterIndex = dbTableName.indexOf('_');
28+
const tableName = dbTableName.slice(schemaDelimiterIndex + 1);
29+
return `${targetSchema}_${tableName}`;
30+
}
31+
32+
updateTableSchema(dbTableName: string, targetSchema: string): Knex.QueryBuilder {
33+
const newDbTableName = this.getMovedDbTableName(dbTableName, targetSchema);
34+
35+
return this.knex.raw('ALTER TABLE ?? RENAME TO ??', [dbTableName, newDbTableName]);
36+
}
37+
}

‎apps/nestjs-backend/src/db-provider/postgres.provider.ts‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ import type { IGroupQueryExtra, IGroupQueryInterface } from './group-query/group
2626
import { GroupQueryPostgres } from './group-query/group-query.postgres';
2727
import type { IntegrityQueryAbstract } from './integrity-query/abstract';
2828
import { IntegrityQueryPostgres } from './integrity-query/integrity-query.postgres';
29+
import type { MoveTableQueryAbstract } from './move-table-query/abstract';
30+
import { MoveTableQueryPostgres } from './move-table-query/move-table-query.postgres';
2931
import { SearchQueryAbstract } from './search-query/abstract';
3032
import { IndexBuilderPostgres } from './search-query/search-index-builder.postgres';
3133
import {
@@ -48,6 +50,10 @@ export class PostgresProvider implements IDbProvider {
4850
];
4951
}
5052

53+
moveTableQuery(queryBuilder: Knex.QueryBuilder): MoveTableQueryAbstract {
54+
return new MoveTableQueryPostgres(queryBuilder);
55+
}
56+
5157
dropSchema(schemaName: string): string {
5258
return this.knex.raw(`DROP SCHEMA IF EXISTS ?? CASCADE`, [schemaName]).toQuery();
5359
}

‎apps/nestjs-backend/src/db-provider/sqlite.provider.ts‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ import type { IGroupQueryExtra, IGroupQueryInterface } from './group-query/group
2626
import { GroupQuerySqlite } from './group-query/group-query.sqlite';
2727
import type { IntegrityQueryAbstract } from './integrity-query/abstract';
2828
import { IntegrityQuerySqlite } from './integrity-query/integrity-query.sqlite';
29+
import type { MoveTableQueryAbstract } from './move-table-query/abstract';
30+
import { MoveTableQuerySqlite } from './move-table-query/move-table-query.sqlite';
2931
import { SearchQueryAbstract } from './search-query/abstract';
3032
import { getOffset } from './search-query/get-offset';
3133
import { IndexBuilderSqlite } from './search-query/search-index-builder.sqlite';
@@ -44,6 +46,10 @@ export class SqliteProvider implements IDbProvider {
4446
return undefined;
4547
}
4648

49+
moveTableQuery(queryBuilder: Knex.QueryBuilder): MoveTableQueryAbstract {
50+
return new MoveTableQuerySqlite(queryBuilder);
51+
}
52+
4753
dropSchema(_schemaName: string) {
4854
return undefined;
4955
}

‎apps/nestjs-backend/src/features/collaborator/collaborator.service.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ export class CollaboratorService {
8888
const query = this.knex
8989
.insert(
9090
collaborators.map((collaborator) => ({
91-
id: getRandomString(16),
91+
id: getRandomString(25),
9292
resource_id: spaceId,
9393
resource_type: CollaboratorType.Space,
9494
role_name: role,
@@ -659,7 +659,7 @@ export class CollaboratorService {
659659
const query = this.knex
660660
.insert(
661661
collaborators.map((collaborator) => ({
662-
id: getRandomString(16),
662+
id: getRandomString(25),
663663
resource_id: baseId,
664664
resource_type: CollaboratorType.Base,
665665
role_name: role,

‎apps/nestjs-backend/src/features/table/open-api/table-open-api.controller.ts‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,12 @@ import {
2525
TableIndex,
2626
duplicateTableRoSchema,
2727
IDuplicateTableRo,
28+
moveTableRoSchema,
29+
IMoveTableRo,
2830
} from '@teable/openapi';
2931
import { ZodValidationPipe } from '../../../zod.validation.pipe';
3032
import { Permissions } from '../../auth/decorators/permissions.decorator';
33+
import { ResourceMeta } from '../../auth/decorators/resource_meta.decorator';
3134
import { TableIndexService } from '../table-index.service';
3235
import { TablePermissionService } from '../table-permission.service';
3336
import { TableService } from '../table.service';
@@ -143,6 +146,18 @@ export class TableController {
143146
return await this.tableOpenApiService.duplicateTable(baseId, tableId, duplicateTableRo);
144147
}
145148

149+
@Permissions('base|update')
150+
@ResourceMeta('baseId', 'params')
151+
@Post(':tableId/move')
152+
async moveTable(
153+
@Param('baseId') baseId: string,
154+
@Param('tableId') tableId: string,
155+
@Body(new ZodValidationPipe(moveTableRoSchema))
156+
moveTableRo: IMoveTableRo
157+
): Promise<{ baseId: string; tableId: string }> {
158+
return await this.tableOpenApiService.moveTable(baseId, tableId, moveTableRo);
159+
}
160+
146161
@Delete(':tableId')
147162
@Permissions('table|delete')
148163
async archiveTable(@Param('baseId') baseId: string, @Param('tableId') tableId: string) {

‎apps/nestjs-backend/src/features/table/open-api/table-open-api.module.ts‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { RecordModule } from '../../record/record.module';
1111
import { ViewOpenApiModule } from '../../view/open-api/view-open-api.module';
1212
import { TableDuplicateService } from '../table-duplicate.service';
1313
import { TableIndexService } from '../table-index.service';
14+
import { TableMoveService } from '../table-move.service';
1415
import { TableModule } from '../table.module';
1516
import { TableController } from './table-open-api.controller';
1617
import { TableOpenApiService } from './table-open-api.service';
@@ -29,7 +30,13 @@ import { TableOpenApiService } from './table-open-api.service';
2930
GraphModule,
3031
],
3132
controllers: [TableController],
32-
providers: [DbProvider, TableOpenApiService, TableIndexService, TableDuplicateService],
33+
providers: [
34+
DbProvider,
35+
TableOpenApiService,
36+
TableIndexService,
37+
TableDuplicateService,
38+
TableMoveService,
39+
],
3340
exports: [TableOpenApiService],
3441
})
3542
export class TableOpenApiModule {}

‎apps/nestjs-backend/src/features/table/open-api/table-open-api.service.ts‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import type {
3131
ICreateTableRo,
3232
ICreateTableWithDefault,
3333
IDuplicateTableRo,
34+
IMoveTableRo,
3435
ITableFullVo,
3536
ITablePermissionVo,
3637
ITableVo,
@@ -39,9 +40,11 @@ import type {
3940
import { Knex } from 'knex';
4041
import { nanoid } from 'nanoid';
4142
import { InjectModel } from 'nest-knexjs';
43+
import { ClsService } from 'nestjs-cls';
4244
import { ThresholdConfig, IThresholdConfig } from '../../../configs/threshold.config';
4345
import { InjectDbProvider } from '../../../db-provider/db.provider';
4446
import { IDbProvider } from '../../../db-provider/db.provider.interface';
47+
import type { IClsStore } from '../../../types/cls';
4548
import { updateOrder } from '../../../utils/update-order';
4649
import { PermissionService } from '../../auth/permission.service';
4750
import { LinkService } from '../../calculation/link.service';
@@ -53,6 +56,7 @@ import { RecordOpenApiService } from '../../record/open-api/record-open-api.serv
5356
import { RecordService } from '../../record/record.service';
5457
import { ViewOpenApiService } from '../../view/open-api/view-open-api.service';
5558
import { TableDuplicateService } from '../table-duplicate.service';
59+
import { TableMoveService } from '../table-move.service';
5660
import { TableService } from '../table.service';
5761

5862
@Injectable()
@@ -70,6 +74,8 @@ export class TableOpenApiService {
7074
private readonly fieldSupplementService: FieldSupplementService,
7175
private readonly permissionService: PermissionService,
7276
private readonly tableDuplicateService: TableDuplicateService,
77+
private readonly tableMoveService: TableMoveService,
78+
private readonly cls: ClsService<IClsStore>,
7379
@InjectDbProvider() private readonly dbProvider: IDbProvider,
7480
@ThresholdConfig() private readonly thresholdConfig: IThresholdConfig,
7581
@InjectModel('CUSTOM_KNEX') private readonly knex: Knex
@@ -210,6 +216,21 @@ export class TableOpenApiService {
210216
return await this.tableDuplicateService.duplicateTable(baseId, tableId, tableRo);
211217
}
212218

219+
async moveTable(baseId: string, tableId: string, tableRo: IMoveTableRo) {
220+
const { baseId: targetBaseId } = tableRo;
221+
await this.checkBaseOwnerPermission(targetBaseId);
222+
return await this.tableMoveService.moveTable(baseId, tableId, tableRo);
223+
}
224+
225+
private async checkBaseOwnerPermission(baseId: string) {
226+
await this.permissionService.validPermissions(baseId, ['table|create']);
227+
228+
const accessTokenId = this.cls.get('accessTokenId');
229+
if (accessTokenId) {
230+
await this.permissionService.validPermissions(baseId, ['table|create'], accessTokenId);
231+
}
232+
}
233+
213234
async createTableMeta(baseId: string, tableRo: ICreateTableRo) {
214235
return await this.tableService.createTable(baseId, tableRo);
215236
}

0 commit comments

Comments
 (0)