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
10 changes: 9 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
{
"cSpell.words": [
"TEABLE"
"clsx",
"countall",
"Datetime",
"lucide",
"oclif",
"openapi",
"tailwindcss",
"TEABLE",
"teatool"
]
}
Binary file modified apps/nextjs-app/public/favicon.ico
Binary file not shown.
2 changes: 1 addition & 1 deletion apps/nextjs-app/src/pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ export default function Home() {
<Loader2 className={`${status === 'submitting' ? 'animate-spin' : ''} mr-2 h-4 w-4`} />
Start
</Button>
{status === 'success' && <Label htmlFor="terms">✅ Let's go check the teable space now! </Label> }
{status === 'success' && <Label htmlFor="terms">✅ Let&apos;s go check the teable space now! </Label> }
{status === 'error' && <Label htmlFor="terms">❌ Oh! Something went wrong! </Label> }
</CardFooter>
</Card>
Expand Down
16 changes: 12 additions & 4 deletions packages/cmd/src/base/migrate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,15 @@ export default class BaseMigrate extends Command {
baseUrl: Flags.string({
description: 'api base url',
}),
fromRps: Flags.integer({
description: 'airtable request per second',
}),
from: Flags.string({
description: 'airtable base id',
}),
toRps: Flags.integer({
description: 'teable request per second',
}),
to: Flags.string({
description: 'teable space id',
}),
Expand All @@ -28,23 +34,25 @@ export default class BaseMigrate extends Command {
}

if (!flags.to) {
throw new Error('Sapce No Set');
throw new Error('Space No Set');
}

const apiMirgrate = new ApiMigrate({
const apiMigrate = new ApiMigrate({
from: {
baseId: flags.from,
airtableToken: airtableToken,
rps: flags.fromRps,
},
to: {
spaceId: flags.to,
teableToken: teableToken,
rps: flags.toRps,
},
baseUrl: flags.baseUrl,
});

await apiMirgrate.execute();
await apiMigrate.execute();

this.log(`base:migrate --from ${flags.from} --to ${flags.to}`);
this.log(`base:migrate --from ${flags.from} --to ${flags.to} --baseUrl ${flags.baseUrl} --fromRps ${flags.fromRps} --toRps ${flags.toRps}`);
}
}
6 changes: 5 additions & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,18 @@
"scripts": {
"format": "prettier --write \"src/**/*.ts\"",
"lint": "eslint \"src/**/*.ts\" --fix",
"build": "tsc -p tsconfig.build.json & tsc-alias -p tsconfig.build.json",
"build": "tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json",
"test": "jest"
},
"dependencies": {
"@teable/core": "^1.5.2",
"@teable/openapi": "^1.5.2",
"axios": "1.6.7",
"axios-rate-limit": "^1.4.0",
"class-transformer": "^0.5.1",
"dayjs": "^1.11.10",
"dotenv": "^16.3.1",
"evt": "^2.5.7",
"lodash": "^4.17.21",
"rxjs": "^7.8.1",
"zod": "^3.22.2"
Expand Down
38 changes: 22 additions & 16 deletions packages/core/src/airtable-sdks/index.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,34 @@
import axios from 'axios';
import axios, { AxiosInstance } from 'axios';
import rateLimit from 'axios-rate-limit';

import { IAirtableTable } from '../types';
import { IAirtableRecordVo, IAirtableTableVo } from './schemas';

export * from './schemas';

export interface ISdkConfig {
airtableToken: string;
rps?: number;
}

export class AirtableSdk {
private airtableToken: string;
private client: AxiosInstance;

constructor(airtableToken: string) {
this.airtableToken = airtableToken;
constructor(config: ISdkConfig) {
this.client = rateLimit(
axios.create({
baseURL: 'https://api.airtable.com',
headers: {
Authorization: `Bearer ${config.airtableToken}`,
},
}),
config.rps ? { maxRPS: config.rps } : {},
);
}

async getTables(baseId: string): Promise<IAirtableTable[]> {
const response = await axios.get<{ tables: IAirtableTableVo[] }>(
`https://api.airtable.com/v0/meta/bases/${baseId}/tables`,
{
headers: {
Authorization: `Bearer ${this.airtableToken}`,
},
},
const response = await this.client.get<{ tables: IAirtableTableVo[] }>(
`/v0/meta/bases/${baseId}/tables`,
);
if (response.status !== 200) {
throw new Error(
Expand All @@ -44,17 +53,14 @@ export class AirtableSdk {
const records: IAirtableRecordVo[] = [];
let offset: string | undefined = '0';
do {
const response = await axios.get<{
const response = await this.client.get<{
offset?: string;
records: IAirtableRecordVo[];
}>(`https://api.airtable.com/v0/${table.baseId}/${table.id}`, {
}>(`/v0/${table.baseId}/${table.id}`, {
params: {
offset,
maxRecords: 1000,
},
headers: {
Authorization: `Bearer ${this.airtableToken}`,
},
});
if (response.status !== 200) {
throw new Error(
Expand Down
56 changes: 29 additions & 27 deletions packages/core/src/api.migrate.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
import {
FieldKeyType,
FieldType,
IFieldRo,
ILinkFieldOptions,
ViewType,
} from '@teable/core';
import { ITableFullVo } from '@teable/openapi';
import axios from 'axios';
import * as _ from 'lodash';

Expand All @@ -13,21 +21,8 @@ import {
IRollupFieldOptionsVo,
} from './airtable-sdks';
import { AirtableFieldVo, getAirtableField } from './models';
import {
ICreateFieldRo,
ILinkFieldOptions,
IRecordsRo,
ITableTableVo,
Table,
TeableSdk,
} from './teable-sdks';
import {
AirtableFieldTypeEnum,
IAirtableTable,
TeableFieldKeyType,
TeableFieldType,
TeableViewTypeEnum,
} from './types';
import { Table, TeableSdk } from './teable-sdks';
import { AirtableFieldTypeEnum, IAirtableTable } from './types';
import { fieldsTopologicalSorting, mappingTable } from './utils';

export class ApiMigrate {
Expand All @@ -39,19 +34,25 @@ export class ApiMigrate {
from: {
airtableToken: string;
baseId: string;
rps?: number;
};
to: {
teableToken: string;
spaceId: string;
rps?: number;
};
baseUrl?: string;
},
) {
this.teableSdk = new TeableSdk({
baseUrl: option.baseUrl,
token: option.to.teableToken,
rps: option.to.rps,
});
this.airtableSdk = new AirtableSdk({
airtableToken: option.from.airtableToken,
rps: option.from.rps,
});
this.airtableSdk = new AirtableSdk(option.from.airtableToken);
axios.interceptors.response.use(
function (response) {
return response;
Expand Down Expand Up @@ -92,7 +93,7 @@ export class ApiMigrate {
const lazy = _.uniq(
fieldDependencies.map((fieldDependency) => fieldDependency[0]),
);
const newTables: ITableTableVo[] = [];
const newTables: ITableFullVo[] = [];
const teableTables: Table[] = [];
const recordIdMapping: Record<string, string> = {};
let i = 1;
Expand Down Expand Up @@ -130,10 +131,9 @@ export class ApiMigrate {
break;
}
}
const teableFieldCreateRos: ICreateFieldRo[] =
appendingAirtableFields.map((field) =>
field.transformTeableCreateFieldRo(tables, newTables),
);
const teableFieldCreateRos: IFieldRo[] = appendingAirtableFields.map(
(field) => field.transformTeableCreateFieldRo(tables, newTables),
);
let j = 1;
const teableTable = await base.createTable({
name: table.name,
Expand All @@ -142,12 +142,12 @@ export class ApiMigrate {
views: table.views.map((view) => {
return {
name: view.name,
type: TeableViewTypeEnum.Grid,
type: ViewType.Grid,
order: j++,
columnMeta: {},
};
}),
fieldKeyType: TeableFieldKeyType.Name,
fieldKeyType: FieldKeyType.Name,
fields: teableFieldCreateRos,
});
await teableTable.deleteRecords(
Expand Down Expand Up @@ -200,7 +200,7 @@ export class ApiMigrate {
fieldDependencies: [string, string][],
tables: IAirtableTable[],
teableTables: Table[],
newTables: ITableTableVo[],
newTables: ITableFullVo[],
) {
const fields = tables.flatMap((table) => table.fields);
const sorting = fieldsTopologicalSorting(fieldDependencies);
Expand Down Expand Up @@ -252,7 +252,7 @@ export class ApiMigrate {
);
}
const covertField = await teableTable.convertField(teableField!.id, {
type: TeableFieldType.Formula,
type: FieldType.Formula,
options: {
expression: formula,
},
Expand All @@ -270,7 +270,7 @@ export class ApiMigrate {
) {
const teableTableVo = teableTable.info;
const linkFields = teableTableVo.fields
.filter((field) => field.type === TeableFieldType.Link)
.filter((field) => field.type === FieldType.Link)
.map((field) => field);
for (const linkField of linkFields) {
const options = linkField.options as ILinkFieldOptions;
Expand Down Expand Up @@ -304,7 +304,9 @@ export class ApiMigrate {
airtableRecords: IAirtableRecordVo[],
airtableFields: AirtableFieldVo[],
recordIdMapping: Record<string, string>,
): IRecordsRo {
): {
fields: Record<string, unknown>;
}[] {
return airtableRecords.map((record) => {
const newRecord: Record<string, any> = {};
for (const fieldName in record.fields) {
Expand Down
27 changes: 27 additions & 0 deletions packages/core/src/datapipes/default-dicscover-catalog-worker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import {
IDiscoverCatalogWorker,
IIntegrationLauncher,
OutputAndStatus,
} from './interfaces';
import {
StandardDiscoverCatalogInput,
StandardDiscoverCatalogOutput,
} from './models';
import { JobStatus } from './protocols';

export class DefaultDiscoverCatalogWorker implements IDiscoverCatalogWorker {
constructor(private readonly integrationLauncher: IIntegrationLauncher) {}

async run(
discoverSchemaInput: StandardDiscoverCatalogInput,
): Promise<OutputAndStatus<StandardDiscoverCatalogOutput>> {
return {
output: { catalog: {} },
status: JobStatus.SUCCESS,
};
}

cancel(): void {
throw new Error('Method not implemented.');
}
}
Loading