Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions apps/nestjs-backend/src/db-provider/db.provider.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import type { IFilterQueryInterface } from './filter-query/filter-query.interfac
import type { IGroupQueryExtra, IGroupQueryInterface } from './group-query/group-query.interface';
import type { IndexBuilderAbstract } from './index-query/index-abstract-builder';
import type { IntegrityQueryAbstract } from './integrity-query/abstract';
import type { MoveTableQueryAbstract } from './move-table-query/abstract';
import type { ISortQueryInterface } from './sort-query/sort-query.interface';

export type IFilterQueryExtra = {
Expand Down Expand Up @@ -62,6 +63,8 @@ export interface IDbProvider {
value: string
): string;

moveTableQuery(queryBuilder: Knex.QueryBuilder): MoveTableQueryAbstract;

updateJsonArrayColumn(
tableName: string,
columnName: string,
Expand Down
16 changes: 16 additions & 0 deletions apps/nestjs-backend/src/db-provider/move-table-query/abstract.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import type { Knex } from 'knex';

export abstract class MoveTableQueryAbstract {
constructor(protected readonly queryBuilder: Knex.QueryBuilder) {}

abstract getSourceBaseJunctionTableName(sourceBaseId: string): Knex.QueryBuilder;

abstract getFullSourceBaseJunctionTableNames(
sourceBaseId: string,
nameFromSqlQuery: string[]
): string[];

abstract getMovedDbTableName(dbTableName: string, targetSchema: string): string;

abstract updateTableSchema(sourceDbTableName: string, targetSchema: string): Knex.QueryBuilder;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import type { Knex } from 'knex';
import { MoveTableQueryAbstract } from './abstract';

export class MoveTableQueryPostgres extends MoveTableQueryAbstract {
protected knex: Knex.Client;
constructor(queryBuilder: Knex.QueryBuilder) {
super(queryBuilder);
this.knex = queryBuilder.client;
}

getSourceBaseJunctionTableName(sourceBaseId: string): Knex.QueryBuilder {
return this.queryBuilder
.select('table_name')
.from('information_schema.tables')
.where('table_schema', sourceBaseId)
.where('table_name', 'like', '%junction_%')
.where('table_type', 'BASE TABLE');
}

getFullSourceBaseJunctionTableNames(sourceBaseId: string, nameFromSqlQuery: string[]): string[] {
if (!Array.isArray(nameFromSqlQuery)) {
return [];
}
return nameFromSqlQuery.map((name) => {
return `${sourceBaseId}.${name}`;
});
}

getMovedDbTableName(dbTableName: string, targetSchema: string): string {
const [, tableName] = dbTableName.split('.');
return `${targetSchema}.${tableName}`;
}

updateTableSchema(sourceDbTableName: string, targetSchema: string): Knex.QueryBuilder {
const [schema, tableName] = sourceDbTableName.split('.');
return this.knex.raw(
`
ALTER TABLE ??.??
SET SCHEMA ??
`,
[schema, tableName, targetSchema]
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import type { Knex } from 'knex';
import { MoveTableQueryAbstract } from './abstract';

export class MoveTableQuerySqlite extends MoveTableQueryAbstract {
protected knex: Knex.Client;
constructor(queryBuilder: Knex.QueryBuilder) {
super(queryBuilder);
this.knex = queryBuilder.client;
}

getSourceBaseJunctionTableName(sourceBaseId: string): Knex.QueryBuilder {
return this.queryBuilder
.select('name as table_name')
.from('sqlite_master')
.where('type', 'table')
.where('name', 'like', `%${sourceBaseId}_junction%`);
}

getFullSourceBaseJunctionTableNames(sourceBaseId: string, nameFromSqlQuery: string[]) {
if (!Array.isArray(nameFromSqlQuery)) {
return [];
}
return nameFromSqlQuery;
}

getMovedDbTableName(dbTableName: string, targetSchema: string): string {
const schemaDelimiterIndex = dbTableName.indexOf('_');
const tableName = dbTableName.slice(schemaDelimiterIndex + 1);
return `${targetSchema}_${tableName}`;
}

updateTableSchema(dbTableName: string, targetSchema: string): Knex.QueryBuilder {
const newDbTableName = this.getMovedDbTableName(dbTableName, targetSchema);

return this.knex.raw('ALTER TABLE ?? RENAME TO ??', [dbTableName, newDbTableName]);
}
}
6 changes: 6 additions & 0 deletions apps/nestjs-backend/src/db-provider/postgres.provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import type { IGroupQueryExtra, IGroupQueryInterface } from './group-query/group
import { GroupQueryPostgres } from './group-query/group-query.postgres';
import type { IntegrityQueryAbstract } from './integrity-query/abstract';
import { IntegrityQueryPostgres } from './integrity-query/integrity-query.postgres';
import type { MoveTableQueryAbstract } from './move-table-query/abstract';
import { MoveTableQueryPostgres } from './move-table-query/move-table-query.postgres';
import { SearchQueryAbstract } from './search-query/abstract';
import { IndexBuilderPostgres } from './search-query/search-index-builder.postgres';
import {
Expand All @@ -48,6 +50,10 @@ export class PostgresProvider implements IDbProvider {
];
}

moveTableQuery(queryBuilder: Knex.QueryBuilder): MoveTableQueryAbstract {
return new MoveTableQueryPostgres(queryBuilder);
}

dropSchema(schemaName: string): string {
return this.knex.raw(`DROP SCHEMA IF EXISTS ?? CASCADE`, [schemaName]).toQuery();
}
Expand Down
6 changes: 6 additions & 0 deletions apps/nestjs-backend/src/db-provider/sqlite.provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import type { IGroupQueryExtra, IGroupQueryInterface } from './group-query/group
import { GroupQuerySqlite } from './group-query/group-query.sqlite';
import type { IntegrityQueryAbstract } from './integrity-query/abstract';
import { IntegrityQuerySqlite } from './integrity-query/integrity-query.sqlite';
import type { MoveTableQueryAbstract } from './move-table-query/abstract';
import { MoveTableQuerySqlite } from './move-table-query/move-table-query.sqlite';
import { SearchQueryAbstract } from './search-query/abstract';
import { getOffset } from './search-query/get-offset';
import { IndexBuilderSqlite } from './search-query/search-index-builder.sqlite';
Expand All @@ -44,6 +46,10 @@ export class SqliteProvider implements IDbProvider {
return undefined;
}

moveTableQuery(queryBuilder: Knex.QueryBuilder): MoveTableQueryAbstract {
return new MoveTableQuerySqlite(queryBuilder);
}

dropSchema(_schemaName: string) {
return undefined;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ export class CollaboratorService {
const query = this.knex
.insert(
collaborators.map((collaborator) => ({
id: getRandomString(16),
id: getRandomString(25),
resource_id: spaceId,
resource_type: CollaboratorType.Space,
role_name: role,
Expand Down Expand Up @@ -659,7 +659,7 @@ export class CollaboratorService {
const query = this.knex
.insert(
collaborators.map((collaborator) => ({
id: getRandomString(16),
id: getRandomString(25),
resource_id: baseId,
resource_type: CollaboratorType.Base,
role_name: role,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,12 @@ import {
TableIndex,
duplicateTableRoSchema,
IDuplicateTableRo,
moveTableRoSchema,
IMoveTableRo,
} from '@teable/openapi';
import { ZodValidationPipe } from '../../../zod.validation.pipe';
import { Permissions } from '../../auth/decorators/permissions.decorator';
import { ResourceMeta } from '../../auth/decorators/resource_meta.decorator';
import { TableIndexService } from '../table-index.service';
import { TablePermissionService } from '../table-permission.service';
import { TableService } from '../table.service';
Expand Down Expand Up @@ -143,6 +146,18 @@ export class TableController {
return await this.tableOpenApiService.duplicateTable(baseId, tableId, duplicateTableRo);
}

@Permissions('base|update')
@ResourceMeta('baseId', 'params')
@Post(':tableId/move')
async moveTable(
@Param('baseId') baseId: string,
@Param('tableId') tableId: string,
@Body(new ZodValidationPipe(moveTableRoSchema))
moveTableRo: IMoveTableRo
): Promise<{ baseId: string; tableId: string }> {
return await this.tableOpenApiService.moveTable(baseId, tableId, moveTableRo);
}

@Delete(':tableId')
@Permissions('table|delete')
async archiveTable(@Param('baseId') baseId: string, @Param('tableId') tableId: string) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { RecordModule } from '../../record/record.module';
import { ViewOpenApiModule } from '../../view/open-api/view-open-api.module';
import { TableDuplicateService } from '../table-duplicate.service';
import { TableIndexService } from '../table-index.service';
import { TableMoveService } from '../table-move.service';
import { TableModule } from '../table.module';
import { TableController } from './table-open-api.controller';
import { TableOpenApiService } from './table-open-api.service';
Expand All @@ -29,7 +30,13 @@ import { TableOpenApiService } from './table-open-api.service';
GraphModule,
],
controllers: [TableController],
providers: [DbProvider, TableOpenApiService, TableIndexService, TableDuplicateService],
providers: [
DbProvider,
TableOpenApiService,
TableIndexService,
TableDuplicateService,
TableMoveService,
],
exports: [TableOpenApiService],
})
export class TableOpenApiModule {}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import type {
ICreateTableRo,
ICreateTableWithDefault,
IDuplicateTableRo,
IMoveTableRo,
ITableFullVo,
ITablePermissionVo,
ITableVo,
Expand All @@ -39,9 +40,11 @@ import type {
import { Knex } from 'knex';
import { nanoid } from 'nanoid';
import { InjectModel } from 'nest-knexjs';
import { ClsService } from 'nestjs-cls';
import { ThresholdConfig, IThresholdConfig } from '../../../configs/threshold.config';
import { InjectDbProvider } from '../../../db-provider/db.provider';
import { IDbProvider } from '../../../db-provider/db.provider.interface';
import type { IClsStore } from '../../../types/cls';
import { updateOrder } from '../../../utils/update-order';
import { PermissionService } from '../../auth/permission.service';
import { LinkService } from '../../calculation/link.service';
Expand All @@ -53,6 +56,7 @@ import { RecordOpenApiService } from '../../record/open-api/record-open-api.serv
import { RecordService } from '../../record/record.service';
import { ViewOpenApiService } from '../../view/open-api/view-open-api.service';
import { TableDuplicateService } from '../table-duplicate.service';
import { TableMoveService } from '../table-move.service';
import { TableService } from '../table.service';

@Injectable()
Expand All @@ -70,6 +74,8 @@ export class TableOpenApiService {
private readonly fieldSupplementService: FieldSupplementService,
private readonly permissionService: PermissionService,
private readonly tableDuplicateService: TableDuplicateService,
private readonly tableMoveService: TableMoveService,
private readonly cls: ClsService<IClsStore>,
@InjectDbProvider() private readonly dbProvider: IDbProvider,
@ThresholdConfig() private readonly thresholdConfig: IThresholdConfig,
@InjectModel('CUSTOM_KNEX') private readonly knex: Knex
Expand Down Expand Up @@ -210,6 +216,21 @@ export class TableOpenApiService {
return await this.tableDuplicateService.duplicateTable(baseId, tableId, tableRo);
}

async moveTable(baseId: string, tableId: string, tableRo: IMoveTableRo) {
const { baseId: targetBaseId } = tableRo;
await this.checkBaseOwnerPermission(targetBaseId);
return await this.tableMoveService.moveTable(baseId, tableId, tableRo);
}

private async checkBaseOwnerPermission(baseId: string) {
await this.permissionService.validPermissions(baseId, ['table|create']);

const accessTokenId = this.cls.get('accessTokenId');
if (accessTokenId) {
await this.permissionService.validPermissions(baseId, ['table|create'], accessTokenId);
}
}

async createTableMeta(baseId: string, tableRo: ICreateTableRo) {
return await this.tableService.createTable(baseId, tableRo);
}
Expand Down
Loading