diff --git a/packages/nc-gui/components/dashboard/TreeView.vue b/packages/nc-gui/components/dashboard/TreeView.vue index c21c60e146..7e29aad957 100644 --- a/packages/nc-gui/components/dashboard/TreeView.vue +++ b/packages/nc-gui/components/dashboard/TreeView.vue @@ -145,7 +145,7 @@ const addTableTab = (table: TableType) => { addTab({ title: table.title, id: table.id, type: table.type as TabType }) } -function openRenameTableDialog(table: TableType, baseId: string, rightClick = false) { +function openRenameTableDialog(table: TableType, baseId?: string, rightClick = false) { $e(rightClick ? 'c:table:rename:navdraw:right-click' : 'c:table:rename:navdraw:options') const isOpen = ref(true) @@ -153,7 +153,7 @@ function openRenameTableDialog(table: TableType, baseId: string, rightClick = fa const { close } = useDialog(resolveComponent('DlgTableRename'), { 'modelValue': isOpen, 'tableMeta': table, - 'baseId': baseId, + 'baseId': baseId || bases.value[0].id, 'onUpdate:modelValue': closeDialog, }) @@ -164,7 +164,7 @@ function openRenameTableDialog(table: TableType, baseId: string, rightClick = fa } } -function openQuickImportDialog(type: string) { +function openQuickImportDialog(type: string, baseId?: string) { $e(`a:actions:import-${type}`) const isOpen = ref(true) @@ -172,6 +172,7 @@ function openQuickImportDialog(type: string) { const { close } = useDialog(resolveComponent('DlgQuickImport'), { 'modelValue': isOpen, 'importType': type, + 'baseId': baseId || bases.value[0].id, 'onUpdate:modelValue': closeDialog, }) @@ -182,13 +183,14 @@ function openQuickImportDialog(type: string) { } } -function openAirtableImportDialog() { +function openAirtableImportDialog(baseId?: string) { $e('a:actions:import-airtable') const isOpen = ref(true) const { close } = useDialog(resolveComponent('DlgAirtableImport'), { 'modelValue': isOpen, + 'baseId': baseId || bases.value[0].id, 'onUpdate:modelValue': closeDialog, }) @@ -200,15 +202,14 @@ function openAirtableImportDialog() { } function openTableCreateDialog(baseId?: string) { - if (!baseId) return $e('c:table:create:navdraw') const isOpen = ref(true) const { close } = useDialog(resolveComponent('DlgTableCreate'), { 'modelValue': isOpen, + 'baseId': baseId || bases.value[0].id, 'onUpdate:modelValue': closeDialog, - 'baseId': baseId, }) function closeDialog() { @@ -301,7 +302,7 @@ useEventListener(document, 'keydown', async (e: KeyboardEvent) => {
@@ -309,14 +310,22 @@ useEventListener(document, 'keydown', async (e: KeyboardEvent) => {
- +
CSV file
- +
JSON file @@ -326,7 +335,7 @@ useEventListener(document, 'keydown', async (e: KeyboardEvent) => {
@@ -460,7 +469,7 @@ useEventListener(document, 'keydown', async (e: KeyboardEvent) => {
@@ -471,7 +480,7 @@ useEventListener(document, 'keydown', async (e: KeyboardEvent) => {
@@ -482,7 +491,7 @@ useEventListener(document, 'keydown', async (e: KeyboardEvent) => {
@@ -493,7 +502,7 @@ useEventListener(document, 'keydown', async (e: KeyboardEvent) => {
diff --git a/packages/nc-gui/components/dlg/AirtableImport.vue b/packages/nc-gui/components/dlg/AirtableImport.vue index a91f37d1ce..12cd859e98 100644 --- a/packages/nc-gui/components/dlg/AirtableImport.vue +++ b/packages/nc-gui/components/dlg/AirtableImport.vue @@ -18,8 +18,9 @@ import { watch, } from '#imports' -const { modelValue } = defineProps<{ +const { modelValue, baseId } = defineProps<{ modelValue: boolean + baseId: string }>() const emit = defineEmits(['update:modelValue']) @@ -100,7 +101,7 @@ async function createOrUpdate() { body: payload, }) } else { - syncSource.value = await $fetch(`/api/v1/db/meta/projects/${project.value.id}/syncs`, { + syncSource.value = await $fetch(`/api/v1/db/meta/projects/${project.value.id}/syncs/${baseId}`, { baseURL, method: 'POST', headers: { 'xc-auth': $state.token.value as string }, @@ -113,7 +114,7 @@ async function createOrUpdate() { } async function loadSyncSrc() { - const data: any = await $fetch(`/api/v1/db/meta/projects/${project.value.id}/syncs`, { + const data: any = await $fetch(`/api/v1/db/meta/projects/${project.value.id}/syncs/${baseId}`, { baseURL, method: 'GET', headers: { 'xc-auth': $state.token.value as string }, diff --git a/packages/nc-gui/components/dlg/QuickImport.vue b/packages/nc-gui/components/dlg/QuickImport.vue index fd8bdd188b..8f3f1bad79 100644 --- a/packages/nc-gui/components/dlg/QuickImport.vue +++ b/packages/nc-gui/components/dlg/QuickImport.vue @@ -27,6 +27,7 @@ import type { importFileList, streamImportFileList } from '~/lib' interface Props { modelValue: boolean importType: 'csv' | 'json' | 'excel' + baseId: string importDataOnly?: boolean } @@ -364,6 +365,7 @@ const beforeUpload = (file: UploadFile) => { :import-data-only="importDataOnly" :quick-import-type="importType" :max-rows-to-parse="importState.parserConfig.maxRowsToParse" + :base-id="baseId" class="nc-quick-import-template-editor" @import="handleImport" /> diff --git a/packages/nc-gui/components/template/Editor.vue b/packages/nc-gui/components/template/Editor.vue index 24fb40b834..2d35badca5 100644 --- a/packages/nc-gui/components/template/Editor.vue +++ b/packages/nc-gui/components/template/Editor.vue @@ -30,7 +30,8 @@ import { } from '#imports' import { TabType } from '~/lib' -const { quickImportType, projectTemplate, importData, importColumns, importDataOnly, maxRowsToParse } = defineProps() +const { quickImportType, projectTemplate, importData, importColumns, importDataOnly, maxRowsToParse, baseId } = + defineProps() const emit = defineEmits(['import']) @@ -45,6 +46,7 @@ interface Props { importColumns: any[] importDataOnly: boolean maxRowsToParse: number + baseId: string } interface Option { @@ -397,7 +399,7 @@ async function importTemplate() { try { isImporting.value = true - const tableName = meta.value?.title + const tableId = meta.value?.id const projectName = project.value.title! await Promise.all( @@ -436,8 +438,8 @@ async function importTemplate() { return res }, {}), ) - await $api.dbTableRow.bulkCreate('noco', projectName, tableName!, batchData) - updateImportTips(projectName, tableName!, progress, total) + await $api.dbTableRow.bulkCreate('noco', projectName, tableId!, batchData) + updateImportTips(projectName, tableId!, progress, total) progress += batchData.length } })(key), @@ -497,8 +499,7 @@ async function importTemplate() { } } } - - const tableMeta = await $api.dbTable.create(project?.value?.id as string, { + const tableMeta = await $api.base.tableCreate(project?.value?.id as string, baseId as string, { table_name: table.table_name, // leave title empty to get a generated one based on table_name title: '', @@ -534,7 +535,7 @@ async function importTemplate() { for (let i = 0; i < data.length; i += offset) { updateImportTips(projectName, tableMeta.title, progress, total) const batchData = remapColNames(data.slice(i, i + offset), tableMeta.columns) - await $api.dbTableRow.bulkCreate('noco', projectName, tableMeta.title, batchData) + await $api.dbTableRow.bulkCreate('noco', projectName, tableMeta.id, batchData) progress += batchData.length } updateImportTips(projectName, tableMeta.title, total, total) diff --git a/packages/nocodb-sdk/src/lib/Api.ts b/packages/nocodb-sdk/src/lib/Api.ts index 9086fd0ea2..682d3e4da6 100644 --- a/packages/nocodb-sdk/src/lib/Api.ts +++ b/packages/nocodb-sdk/src/lib/Api.ts @@ -1988,6 +1988,32 @@ export class Api< ...params, }), + /** + * No description + * + * @tags Base + * @name TableList + * @request GET:/api/v1/db/meta/projects/{projectId}/{baseId}/tables + * @response `200` `TableListType` + */ + tableList: ( + projectId: string, + baseId: string, + query?: { + page?: number; + pageSize?: number; + sort?: string; + includeM2M?: boolean; + }, + params: RequestParams = {} + ) => + this.request({ + path: `/api/v1/db/meta/projects/${projectId}/${baseId}/tables`, + method: 'GET', + query: query, + ...params, + }), + /** * No description * diff --git a/packages/nocodb/src/lib/meta/api/sync/helpers/job.ts b/packages/nocodb/src/lib/meta/api/sync/helpers/job.ts index 58eacc7495..8f607b3bf6 100644 --- a/packages/nocodb/src/lib/meta/api/sync/helpers/job.ts +++ b/packages/nocodb/src/lib/meta/api/sync/helpers/job.ts @@ -214,7 +214,7 @@ export default async ( } function getRootDbType() { - return ncCreatedProjectSchema?.bases[0]?.type; + return ncCreatedProjectSchema?.bases.find((el) => el.id === syncDB.baseId)?.type; } // base mapping table @@ -312,7 +312,7 @@ export default async ( // @ts-ignore async function nc_DumpTableSchema() { console.log('['); - const ncTblList = await api.dbTable.list(ncCreatedProjectSchema.id); + const ncTblList = await api.base.tableList(ncCreatedProjectSchema.id, syncDB.baseId); for (let i = 0; i < ncTblList.list.length; i++) { const ncTbl = await api.dbTable.read(ncTblList.list[i].id); console.log(JSON.stringify(ncTbl, null, 2)); @@ -611,11 +611,12 @@ export default async ( for (let idx = 0; idx < tables.length; idx++) { logBasic(`:: [${idx + 1}/${tables.length}] ${tables[idx].title}`); - logDetailed(`NC API: dbTable.create ${tables[idx].title}`); + logDetailed(`NC API: base.tableCreate ${tables[idx].title}`); let _perfStart = recordPerfStart(); - const table: any = await api.dbTable.create( + const table: any = await api.base.tableCreate( ncCreatedProjectSchema.id, + syncDB.baseId, tables[idx] ); recordPerfStats(_perfStart, 'dbTable.create'); @@ -2171,6 +2172,7 @@ export default async ( } else { await nocoGetProject(syncDB.projectId); syncDB.projectName = ncCreatedProjectSchema?.title; + syncDB.baseId = syncDB.baseId || ncCreatedProjectSchema.bases[0].id; logDetailed('Getting existing project meta'); } @@ -2228,8 +2230,8 @@ export default async ( try { // await nc_DumpTableSchema(); const _perfStart = recordPerfStart(); - const ncTblList = await api.dbTable.list(ncCreatedProjectSchema.id); - recordPerfStats(_perfStart, 'dbTable.list'); + const ncTblList = await api.base.tableList(ncCreatedProjectSchema.id, syncDB.baseId); + recordPerfStats(_perfStart, 'base.tableList'); logBasic('Reading Records...'); @@ -2385,6 +2387,7 @@ export interface AirtableSyncConfig { authToken: string; projectName?: string; projectId?: string; + baseId?: string; apiKey: string; shareId: string; options: { diff --git a/packages/nocodb/src/lib/meta/api/sync/importApis.ts b/packages/nocodb/src/lib/meta/api/sync/importApis.ts index e1a56cf3a1..5b3d05bcb9 100644 --- a/packages/nocodb/src/lib/meta/api/sync/importApis.ts +++ b/packages/nocodb/src/lib/meta/api/sync/importApis.ts @@ -111,6 +111,7 @@ export default ( id: req.params.syncId, ...(syncSource?.details || {}), projectId: syncSource.project_id, + baseId: syncSource.base_id, authToken: token, baseURL, }); diff --git a/packages/nocodb/src/lib/meta/api/sync/syncSourceApis.ts b/packages/nocodb/src/lib/meta/api/sync/syncSourceApis.ts index 48549044f1..f261c0cebd 100644 --- a/packages/nocodb/src/lib/meta/api/sync/syncSourceApis.ts +++ b/packages/nocodb/src/lib/meta/api/sync/syncSourceApis.ts @@ -4,17 +4,21 @@ import SyncSource from '../../../models/SyncSource'; import { Tele } from 'nc-help'; import { PagedResponseImpl } from '../../helpers/PagedResponse'; import ncMetaAclMw from '../../helpers/ncMetaAclMw'; +import Project from '../../../models/Project'; export async function syncSourceList(req: Request, res: Response) { // todo: pagination - res.json(new PagedResponseImpl(await SyncSource.list(req.params.projectId))); + res.json(new PagedResponseImpl(await SyncSource.list(req.params.projectId, req.params.baseId))); } export async function syncCreate(req: Request, res: Response) { Tele.emit('evt', { evt_type: 'webhooks:created' }); + const project = await Project.getWithInfo(req.params.projectId); + const sync = await SyncSource.insert({ ...req.body, fk_user_id: (req as any).user.id, + base_id: req.params.baseId ? req.params.baseId : project.bases[0].id, project_id: req.params.projectId, }); res.json(sync); @@ -41,6 +45,14 @@ router.post( '/api/v1/db/meta/projects/:projectId/syncs', ncMetaAclMw(syncCreate, 'syncSourceCreate') ); +router.get( + '/api/v1/db/meta/projects/:projectId/syncs/:baseId', + ncMetaAclMw(syncSourceList, 'syncSourceList') +); +router.post( + '/api/v1/db/meta/projects/:projectId/syncs/:baseId', + ncMetaAclMw(syncCreate, 'syncSourceCreate') +); router.delete( '/api/v1/db/meta/syncs/:syncId', ncMetaAclMw(syncDelete, 'syncSourceDelete') diff --git a/packages/nocodb/src/lib/meta/api/tableApis.ts b/packages/nocodb/src/lib/meta/api/tableApis.ts index 89df8a5072..e9aa9442d3 100644 --- a/packages/nocodb/src/lib/meta/api/tableApis.ts +++ b/packages/nocodb/src/lib/meta/api/tableApis.ts @@ -187,6 +187,7 @@ export async function tableCreate(req: Request, res) { await Audit.insert({ project_id: project.id, + base_id: base.id, op_type: AuditOperationTypes.TABLE, op_sub_type: AuditOperationSubTypes.CREATED, user: (req as any)?.user?.email, @@ -357,6 +358,7 @@ export async function tableDelete(req: Request, res: Response) { await Audit.insert({ project_id: project.id, + base_id: base.id, op_type: AuditOperationTypes.TABLE, op_sub_type: AuditOperationSubTypes.DELETED, user: (req as any)?.user?.email, @@ -375,6 +377,11 @@ router.get( metaApiMetrics, ncMetaAclMw(tableList, 'tableList') ); +router.get( + '/api/v1/db/meta/projects/:projectId/:baseId/tables', + metaApiMetrics, + ncMetaAclMw(tableList, 'tableList') +); router.post( '/api/v1/db/meta/projects/:projectId/tables', metaApiMetrics, diff --git a/packages/nocodb/src/lib/migrations/XcMigrationSourcev2.ts b/packages/nocodb/src/lib/migrations/XcMigrationSourcev2.ts index 280825dddc..e3ee86760f 100644 --- a/packages/nocodb/src/lib/migrations/XcMigrationSourcev2.ts +++ b/packages/nocodb/src/lib/migrations/XcMigrationSourcev2.ts @@ -10,6 +10,7 @@ import * as nc_019_add_meta_in_meta_tables from './v2/nc_019_add_meta_in_meta_ta import * as nc_020_kanban_view from './v2/nc_020_kanban_view'; import * as nc_021_add_fields_in_token from './v2/nc_021_add_fields_in_token'; import * as nc_022_qr_code_column_type from './v2/nc_022_qr_code_column_type'; +import * as nc_023_add_base_id_in_sync_source from './v2/nc_023_add_base_id_in_sync_source'; // Create a custom migration source class export default class XcMigrationSourcev2 { @@ -31,6 +32,7 @@ export default class XcMigrationSourcev2 { 'nc_020_kanban_view', 'nc_021_add_fields_in_token', 'nc_022_qr_code_column_type', + 'nc_023_add_base_id_in_sync_source' ]); } @@ -64,6 +66,8 @@ export default class XcMigrationSourcev2 { return nc_021_add_fields_in_token; case 'nc_022_qr_code_column_type': return nc_022_qr_code_column_type; + case 'nc_023_add_base_id_in_sync_source': + return nc_023_add_base_id_in_sync_source; } } } diff --git a/packages/nocodb/src/lib/migrations/v2/nc_023_add_base_id_in_sync_source.ts b/packages/nocodb/src/lib/migrations/v2/nc_023_add_base_id_in_sync_source.ts new file mode 100644 index 0000000000..b29dd2814d --- /dev/null +++ b/packages/nocodb/src/lib/migrations/v2/nc_023_add_base_id_in_sync_source.ts @@ -0,0 +1,39 @@ +import Knex from 'knex'; +import { MetaTable } from '../../utils/globals'; + +const up = async (knex: Knex) => { + await knex.schema.alterTable(MetaTable.SYNC_SOURCE, (table) => { + table.string('base_id', 20); + table.foreign('base_id').references(`${MetaTable.BASES}.id`); + }); +}; + +const down = async (knex) => { + await knex.schema.alterTable(MetaTable.SYNC_SOURCE, (table) => { + table.dropColumn('base_id'); + }); +}; + +export { up, down }; + +/** + * @copyright Copyright (c) 2022, Xgene Cloud Ltd + * + * @author Mert Ersoy + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ diff --git a/packages/nocodb/src/lib/models/SyncSource.ts b/packages/nocodb/src/lib/models/SyncSource.ts index 39f7bf7489..9a5bf685f0 100644 --- a/packages/nocodb/src/lib/models/SyncSource.ts +++ b/packages/nocodb/src/lib/models/SyncSource.ts @@ -12,6 +12,7 @@ export default class SyncSource { deleted?: boolean; order?: number; project_id?: string; + base_id?: string; fk_user_id?: string; constructor(syncSource: Partial) { @@ -37,15 +38,14 @@ export default class SyncSource { return syncSource && new SyncSource(syncSource); } - static async list(projectId: string, ncMeta = Noco.ncMeta) { + static async list(projectId: string, baseId?: string, ncMeta = Noco.ncMeta) { + const condition = baseId ? { project_id: projectId, base_id: baseId } : { project_id: projectId }; const syncSources = await ncMeta.metaList( null, null, MetaTable.SYNC_SOURCE, { - condition: { - project_id: projectId, - }, + condition, orderBy: { created_at: 'asc', }, @@ -77,6 +77,7 @@ export default class SyncSource { type: syncSource?.type, details: syncSource?.details, project_id: syncSource?.project_id, + base_id: syncSource?.base_id, fk_user_id: syncSource?.fk_user_id, }; @@ -107,6 +108,7 @@ export default class SyncSource { 'deleted', 'order', 'project_id', + 'base_id', ]); if (updateObj.details && typeof updateObj.details === 'object') { diff --git a/scripts/sdk/swagger.json b/scripts/sdk/swagger.json index 8f3842cc35..79828584c2 100644 --- a/scripts/sdk/swagger.json +++ b/scripts/sdk/swagger.json @@ -1743,6 +1743,55 @@ "required": true } ], + "get": { + "summary": "", + "operationId": "table-list", + "responses": { + "200": { + "$ref": "#/components/responses/TableList" + } + }, + "parameters": [ + { + "schema": { + "type": "string" + }, + "in": "header", + "name": "xc-auth" + }, + { + "schema": { + "type": "number" + }, + "in": "query", + "name": "page" + }, + { + "schema": { + "type": "number" + }, + "in": "query", + "name": "pageSize" + }, + { + "schema": { + "type": "string" + }, + "in": "query", + "name": "sort" + }, + { + "schema": { + "type": "boolean" + }, + "in": "query", + "name": "includeM2M" + } + ], + "tags": [ + "Base" + ] + }, "post": { "summary": "", "operationId": "table-create",