Browse Source

Merge pull request #8510 from nocodb/nc-refactor/ds

Nc refactor/ds
pull/8516/head
Pranav C 6 months ago committed by GitHub
parent
commit
c7cc1f92fd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 3
      packages/nc-gui/components/dashboard/TreeView/TableNode.vue
  2. 584
      packages/nc-gui/components/dashboard/settings/DataSources.vue
  3. 107
      packages/nc-gui/components/dashboard/settings/Modal.vue
  4. 2
      packages/nc-gui/components/dashboard/settings/UIAcl.vue
  5. 53
      packages/nc-gui/components/dlg/ProjectAudit.vue
  6. 6
      packages/nc-gui/components/project/AllTables.vue
  7. 4
      packages/nc-gui/components/project/View.vue
  8. 2
      packages/nc-gui/lang/en.json
  9. 8
      packages/noco-docs/docs/100.data-sources/010.data-source-overview.md
  10. 29
      packages/noco-docs/docs/100.data-sources/020.connect-to-data-source.md
  11. 18
      packages/noco-docs/docs/100.data-sources/030.sync-with-data-source.md
  12. 69
      packages/noco-docs/docs/100.data-sources/040.actions-on-data-sources.md
  13. BIN
      packages/noco-docs/static/img/v2/data-source/data-source-1.png
  14. BIN
      packages/noco-docs/static/img/v2/data-source/data-source-2.png
  15. BIN
      packages/noco-docs/static/img/v2/data-source/data-source-audit.png
  16. BIN
      packages/noco-docs/static/img/v2/data-source/data-source-edit.png
  17. BIN
      packages/noco-docs/static/img/v2/data-source/data-source-erd.png
  18. BIN
      packages/noco-docs/static/img/v2/data-source/data-source-hide.png
  19. BIN
      packages/noco-docs/static/img/v2/data-source/data-source-meta-sync-1.png
  20. BIN
      packages/noco-docs/static/img/v2/data-source/data-source-meta-sync-2.png
  21. BIN
      packages/noco-docs/static/img/v2/data-source/data-source-remove.png
  22. BIN
      packages/noco-docs/static/img/v2/data-source/data-source-uiacl.png
  23. BIN
      packages/noco-docs/static/img/v2/data-source/ds-connect-1.png
  24. BIN
      packages/noco-docs/static/img/v2/data-source/ds-connect-2.png
  25. BIN
      packages/noco-docs/static/img/v2/data-source/ds-connect-3.png
  26. BIN
      packages/noco-docs/static/img/v2/data-source/ds-connect-4.png
  27. 4
      tests/playwright/pages/Dashboard/ProjectView/Metadata.ts
  28. 3
      tests/playwright/pages/Dashboard/ProjectView/index.ts
  29. 30
      tests/playwright/pages/Dashboard/Settings/DataSources.ts
  30. 4
      tests/playwright/pages/Dashboard/TreeView.ts
  31. 6
      tests/playwright/tests/db/features/erd.spec.ts
  32. 13
      tests/playwright/tests/db/features/metaSync.spec.ts
  33. 6
      tests/playwright/tests/db/general/tableOperations.spec.ts

3
packages/nc-gui/components/dashboard/TreeView/TableNode.vue

@ -331,6 +331,7 @@ const deleteTable = () => {
<NcMenuItem <NcMenuItem
v-if="isUIAllowed('tableRename', { roles: baseRole })" v-if="isUIAllowed('tableRename', { roles: baseRole })"
:data-testid="`sidebar-table-rename-${table.title}`" :data-testid="`sidebar-table-rename-${table.title}`"
class="nc-table-rename"
@click="openRenameTableDialog(table, base.sources[sourceIndex].id)" @click="openRenameTableDialog(table, base.sources[sourceIndex].id)"
> >
<div v-e="['c:table:rename']" class="flex gap-2 items-center"> <div v-e="['c:table:rename']" class="flex gap-2 items-center">
@ -358,7 +359,7 @@ const deleteTable = () => {
<NcMenuItem <NcMenuItem
v-if="isUIAllowed('tableDelete', { roles: baseRole })" v-if="isUIAllowed('tableDelete', { roles: baseRole })"
:data-testid="`sidebar-table-delete-${table.title}`" :data-testid="`sidebar-table-delete-${table.title}`"
class="!text-red-500 !hover:bg-red-50" class="!text-red-500 !hover:bg-red-50 nc-table-delete"
@click="deleteTable" @click="deleteTable"
> >
<div v-e="['c:table:delete']" class="flex gap-2 items-center"> <div v-e="['c:table:delete']" class="flex gap-2 items-center">

584
packages/nc-gui/components/dashboard/settings/DataSources.vue

@ -25,6 +25,8 @@ const { isDataSourceLimitReached } = storeToRefs(basesStore)
const baseStore = useBase() const baseStore = useBase()
const { base } = storeToRefs(baseStore) const { base } = storeToRefs(baseStore)
const { isUIAllowed } = useRoles()
const { projectPageTab } = storeToRefs(useConfigStore()) const { projectPageTab } = storeToRefs(useConfigStore())
const { refreshCommandPalette } = useCommandPalette() const { refreshCommandPalette } = useCommandPalette()
@ -253,400 +255,239 @@ const isNewBaseModalOpen = computed({
}, },
}) })
const isErdModalOpen = computed({ const activeSource = ref<SourceType>(null)
get: () => { const openedTab = ref('erd')
return [DataSourcesSubTab.ERD].includes(vState.value as any)
},
set: (val) => {
if (!val) {
vState.value = ''
}
},
})
const isMetaDataModal = computed({
get: () => {
return [DataSourcesSubTab.Metadata].includes(vState.value as any)
},
set: (val) => {
if (!val) {
vState.value = ''
}
},
})
const isUIAclModalOpen = computed({
get: () => {
return [DataSourcesSubTab.UIAcl].includes(vState.value as any)
},
set: (val) => {
if (!val) {
vState.value = ''
}
},
})
const isBaseAuditModalOpen = computed({
get: () => {
return [DataSourcesSubTab.Audit].includes(vState.value as any)
},
set: (val) => {
if (!val) {
vState.value = ''
}
},
})
const isEditBaseModalOpen = computed({
get: () => {
return [DataSourcesSubTab.Edit].includes(vState.value as any)
},
set: (val) => {
if (!val) {
vState.value = ''
}
},
})
</script> </script>
<template> <template>
<div class="flex flex-row w-full h-full nc-data-sources-view"> <div class="flex flex-col h-full">
<div class="flex flex-col w-full overflow-auto"> <div class="px-4 py-2 flex justify-between">
<div class="flex flex-row w-full justify-end mt-6.5 mb-2"> <a-breadcrumb separator=">" class="w-full cursor-pointer font-weight-bold">
<NcButton <a-breadcrumb-item @click="activeSource = null">
v-if="!isDataSourceLimitReached" <a class="!no-underline">Data Sources</a>
size="large" </a-breadcrumb-item>
class="z-10 !px-2" <a-breadcrumb-item v-if="activeSource">
type="primary" <span class="capitalize">{{ activeSource.alias || 'Default Source' }}</span>
@click="vState = DataSourcesSubTab.New" </a-breadcrumb-item>
> </a-breadcrumb>
<div class="flex flex-row items-center w-full gap-x-1">
<component :is="iconMap.plus" /> <NcButton
<div class="flex">{{ $t('activity.newSource') }}</div> v-if="!isDataSourceLimitReached && !activeSource && isUIAllowed('sourceCreate')"
</div> size="large"
</NcButton> class="z-10 !px-2"
</div> type="primary"
<div @click="vState = DataSourcesSubTab.New"
class="overflow-y-auto nc-scrollbar-md"
:style="{
maxHeight: 'calc(100vh - 200px)',
}"
> >
<div class="ds-table-head"> <div class="flex flex-row items-center w-full gap-x-1">
<div class="ds-table-row"> <component :is="iconMap.plus" />
<div class="ds-table-col ds-table-enabled cursor-pointer">{{ $t('general.visibility') }}</div> <div class="flex">{{ $t('activity.newSource') }}</div>
<div class="ds-table-col ds-table-name">{{ $t('general.name') }}</div>
<div class="ds-table-col ds-table-type">{{ $t('general.type') }}</div>
<div class="ds-table-col ds-table-actions -ml-13">{{ $t('labels.actions') }}</div>
<div class="ds-table-col ds-table-crud"></div>
</div>
</div> </div>
<div class="ds-table-body"> </NcButton>
<Draggable :list="sources" item-key="id" handle=".ds-table-handle" @end="moveBase"> </div>
<template #header> <div data-testid="nc-settings-datasources" class="flex flex-row w-full nc-data-sources-view flex-grow min-h-0">
<div v-if="sources[0]" class="ds-table-row border-gray-200"> <template v-if="activeSource">
<div class="ds-table-col ds-table-enabled"> <NcTabs v-model:activeKey="openedTab" class="nc-source-tab w-full">
<div class="flex items-center gap-1"> <a-tab-pane key="erd">
<div v-if="sources.length > 2" class="ds-table-handle" /> <template #tab>
<a-tooltip> <div class="tab" data-testid="nc-erd-tab">
<template #title> <div>{{ $t('title.erdView') }}</div>
<template v-if="sources[0].enabled">{{ $t('activity.hideInUI') }}</template> </div>
<template v-else>{{ $t('activity.showInUI') }}</template> </template>
</template> <div class="h-full pt-4">
<a-switch <LazyDashboardSettingsErd class="h-full overflow-auto" :source-id="activeSource.id" :show-all-columns="false" />
:checked="sources[0].enabled ? true : false" </div>
class="cursor-pointer" </a-tab-pane>
size="small" <a-tab-pane v-if="sources && activeSource === sources[0]" key="audit">
@change="toggleBase(sources[0], $event)" <template #tab>
/> <div class="tab" data-testid="nc-audit-tab">
</a-tooltip> <div>{{ $t('title.auditLogs') }}</div>
</div> </div>
</div> </template>
<div class="ds-table-col ds-table-name font-medium"> <div class="p-4 h-full overflow-auto">
<div class="flex items-center gap-1"> <LazyDashboardSettingsBaseAudit :source-id="activeSource.id" />
<!-- <GeneralBaseLogo :base-type="sources[0].type" /> --> </div>
{{ $t('general.default') }} </a-tab-pane>
</div> <a-tab-pane v-if="!activeSource.is_meta && !activeSource.is_local" key="audit">
</div> <template #tab>
<div class="tab" data-testid="nc-connection-tab">
<div>{{ $t('labels.connectionDetails') }}</div>
</div>
</template>
<div class="p-6 mt-4 h-full overflow-auto">
<LazyDashboardSettingsDataSourcesEditBase
class="w-600px"
:source-id="activeSource.id"
@source-updated="loadBases(true)"
@close="activeSource = null"
/>
</div>
</a-tab-pane>
<div class="ds-table-col ds-table-type"> <a-tab-pane key="acl">
<div class="flex items-center gap-1">-</div> <template #tab>
</div> <div class="tab" data-testid="nc-acl-tab">
<div>{{ $t('labels.uiAcl') }}</div>
</div>
</template>
<div class="ds-table-col ds-table-actions"> <div class="pt-4 h-full overflow-auto">
<div class="flex items-center gap-2"> <LazyDashboardSettingsUIAcl :source-id="activeSource.id" />
<NcTooltip v-if="!sources[0].is_meta && !sources[0].is_local"> </div>
<template #title> </a-tab-pane>
{{ $t('tooltip.metaSync') }} <a-tab-pane v-if="!activeSource.is_meta && !activeSource.is_local" key="meta-sync">
</template> <template #tab>
<NcButton <div class="tab" data-testid="nc-meta-sync-tab">
class="nc-action-btn cursor-pointer outline-0" <div>{{ $t('labels.metaSync') }}</div>
type="text"
data-testid="nc-data-sources-view-meta-sync"
size="small"
@click="baseAction(sources[0].id, DataSourcesSubTab.Metadata)"
>
<div class="flex items-center gap-2 text-gray-600">
<GeneralIcon icon="sync" class="group-hover:text-accent" />
</div>
</NcButton>
</NcTooltip>
<NcTooltip>
<template #title>
{{ $t('title.relations') }}
</template>
<NcButton
size="small"
class="nc-action-btn cursor-pointer outline-0"
type="text"
data-testid="nc-data-sources-view-erd"
@click="baseAction(sources[0].id, DataSourcesSubTab.ERD)"
>
<div class="flex items-center gap-2 text-gray-600">
<GeneralIcon icon="erd" class="group-hover:text-accent" />
</div>
</NcButton>
</NcTooltip>
<NcTooltip>
<template #title>
{{ $t('labels.uiAcl') }}
</template>
<NcButton
size="small"
class="nc-action-btn cursor-pointer outline-0"
type="text"
data-testid="nc-data-sources-view-ui-acl"
@click="baseAction(sources[0].id, DataSourcesSubTab.UIAcl)"
>
<div class="flex items-center gap-2 text-gray-600">
<GeneralIcon icon="acl" class="group-hover:text-accent" />
</div>
</NcButton>
</NcTooltip>
<NcTooltip>
<template #title>
{{ $t('title.audit') }}
</template>
<NcButton
size="small"
class="nc-action-btn cursor-pointer outline-0"
type="text"
data-testid="nc-data-sources-view-audit"
@click="baseAction(sources[0].id, DataSourcesSubTab.Audit)"
>
<div class="flex items-center gap-2 text-gray-600">
<GeneralIcon icon="book" class="group-hover:text-accent" />
</div>
</NcButton>
</NcTooltip>
</div>
</div>
<div class="ds-table-col ds-table-crud">
<NcButton
v-if="!sources[0].is_meta && !sources[0].is_local"
size="small"
class="nc-action-btn cursor-pointer outline-0 !w-8 !px-1 !rounded-lg"
type="text"
@click="baseAction(sources[0].id, DataSourcesSubTab.Edit)"
>
<GeneralIcon icon="edit" class="text-gray-600" />
</NcButton>
</div>
</div> </div>
</template> </template>
<template #item="{ element: source, index }"> <div class="pt-4 h-full overflow-auto">
<div v-if="index !== 0" class="ds-table-row border-gray-200"> <LazyDashboardSettingsMetadata :source-id="activeSource.id" @source-synced="loadBases(true)" />
<div class="ds-table-col ds-table-enabled"> </div>
<div class="flex items-center gap-1"> </a-tab-pane>
<GeneralIcon v-if="sources.length > 2" icon="dragVertical" small class="ds-table-handle" /> </NcTabs>
<a-tooltip> </template>
<template #title> <div v-else class="flex flex-col w-full overflow-auto mt-1">
<template v-if="source.enabled">{{ $t('activity.hideInUI') }}</template> <div
<template v-else>{{ $t('activity.showInUI') }}</template> class="overflow-y-auto nc-scrollbar-md"
</template> :style="{
<a-switch maxHeight: 'calc(100vh - 200px)',
:checked="source.enabled ? true : false" }"
class="cursor-pointer" >
size="small" <div class="ds-table-head">
@change="toggleBase(source, $event)" <div class="ds-table-row">
/> <div class="ds-table-col ds-table-enabled cursor-pointer">{{ $t('general.visibility') }}</div>
</a-tooltip> <div class="ds-table-col ds-table-name">{{ $t('general.name') }}</div>
<div class="ds-table-col ds-table-type">{{ $t('general.type') }}</div>
<div class="ds-table-col ds-table-actions">{{ $t('labels.actions') }}</div>
</div>
</div>
<div class="ds-table-body">
<Draggable :list="sources" item-key="id" handle=".ds-table-handle" @end="moveBase">
<template #header>
<div v-if="sources[0]" class="ds-table-row border-gray-200 cursor-pointer" @click="activeSource = sources[0]">
<div class="ds-table-col ds-table-enabled">
<div class="flex items-center gap-1" @click.stop>
<div v-if="sources.length > 2" class="ds-table-handle" />
<a-tooltip>
<template #title>
<template v-if="sources[0].enabled">{{ $t('activity.hideInUI') }}</template>
<template v-else>{{ $t('activity.showInUI') }}</template>
</template>
<a-switch
:checked="sources[0].enabled ? true : false"
class="cursor-pointer"
size="small"
@change="toggleBase(sources[0], $event)"
/>
</a-tooltip>
</div>
</div>
<div class="ds-table-col ds-table-name font-medium">
<div class="flex items-center gap-1">
<!-- <GeneralBaseLogo :base-type="sources[0].type" /> -->
{{ $t('general.default') }}
</div>
</div> </div>
</div>
<div class="ds-table-col ds-table-name font-medium w-full">
<div v-if="source.is_meta || source.is_local">-</div>
<span v-else class="truncate">
{{ source.is_meta || source.is_local ? $t('general.base') : source.alias }}
</span>
</div>
<div class="ds-table-col ds-table-type"> <div class="ds-table-col ds-table-type">
<div class="flex items-center gap-2"> <div class="flex items-center gap-1">-</div>
<GeneralBaseLogo :source-type="source.type" /> </div>
<span class="text-gray-700 capitalize">{{ source.type }}</span>
<div class="ds-table-col ds-table-actions">
<NcButton
v-if="!sources[0].is_meta && !sources[0].is_local"
size="small"
class="nc-action-btn cursor-pointer outline-0 !w-8 !px-1 !rounded-lg"
type="text"
@click.stop="baseAction(sources[0].id, DataSourcesSubTab.Edit)"
>
<GeneralIcon icon="edit" class="text-gray-600" />
</NcButton>
</div> </div>
</div> </div>
</template>
<template #item="{ element: source, index }">
<div v-if="index !== 0" class="ds-table-row border-gray-200 cursor-pointer" @click="activeSource = source">
<div class="ds-table-col ds-table-enabled">
<div class="flex items-center gap-1" @click.stop>
<GeneralIcon v-if="sources.length > 2" icon="dragVertical" small class="ds-table-handle" />
<a-tooltip>
<template #title>
<template v-if="source.enabled">{{ $t('activity.hideInUI') }}</template>
<template v-else>{{ $t('activity.showInUI') }}</template>
</template>
<a-switch
:checked="source.enabled ? true : false"
class="cursor-pointer"
size="small"
@change="toggleBase(source, $event)"
/>
</a-tooltip>
</div>
</div>
<div class="ds-table-col ds-table-name font-medium w-full">
<div v-if="source.is_meta || source.is_local" class="h-8 w-1">-</div>
<span v-else class="truncate">
{{ source.is_meta || source.is_local ? $t('general.base') : source.alias }}
</span>
</div>
<div class="ds-table-col ds-table-actions"> <div class="ds-table-col ds-table-type">
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<NcTooltip> <GeneralBaseLogo :source-type="source.type" />
<template #title> <span class="text-gray-700 capitalize">{{ source.type }}</span>
{{ $t('title.relations') }} </div>
</template> </div>
<NcButton <div class="ds-table-col justify-end gap-x-1 ds-table-actions">
size="small"
class="nc-action-btn cursor-pointer outline-0"
type="text"
data-testid="nc-data-sources-view-erd"
@click="baseAction(source.id, DataSourcesSubTab.ERD)"
>
<div class="flex items-center gap-2 text-gray-600">
<GeneralIcon icon="erd" class="group-hover:text-accent" />
</div>
</NcButton>
</NcTooltip>
<NcTooltip>
<template #title>
{{ $t('labels.uiAcl') }}
</template>
<NcButton
size="small"
type="text"
class="nc-action-btn cursor-pointer outline-0"
data-testid="nc-data-sources-view-ui-acl"
@click="baseAction(source.id, DataSourcesSubTab.UIAcl)"
>
<div class="flex items-center gap-2 text-gray-600">
<GeneralIcon icon="acl" class="group-hover:text-accent" />
</div>
</NcButton>
</NcTooltip>
<NcTooltip v-if="!isEeUI">
<template #title>
{{ $t('title.audit') }}
</template>
<NcButton
size="small"
class="nc-action-btn cursor-pointer outline-0"
type="text"
data-testid="nc-data-sources-view-audit"
@click="baseAction(source.id, DataSourcesSubTab.Audit)"
>
<div class="flex items-center gap-2 text-gray-600">
<GeneralIcon icon="book" class="group-hover:text-accent" />
</div>
</NcButton>
</NcTooltip>
<NcTooltip> <NcTooltip>
<template #title> <template #title>
{{ $t('tooltip.metaSync') }} {{ $t('general.remove') }}
</template> </template>
<NcButton <NcButton
v-if="!source.is_meta && !source.is_local" v-if="!source.is_meta && !source.is_local"
size="small" size="small"
class="nc-action-btn cursor-pointer outline-0 !w-8 !px-1 !rounded-lg"
type="text" type="text"
data-testid="nc-data-sources-view-meta-sync" @click.stop="openDeleteBase(source)"
class="nc-action-btn cursor-pointer outline-0"
@click="baseAction(source.id, DataSourcesSubTab.Metadata)"
> >
<div class="flex items-center gap-2 text-gray-600"> <GeneralIcon icon="delete" class="text-red-500" />
<GeneralIcon icon="sync" class="group-hover:text-accent" />
</div>
</NcButton> </NcButton>
</NcTooltip> </NcTooltip>
</div> </div>
</div> </div>
<div class="ds-table-col ds-table-crud justify-end gap-x-1"> </template>
<NcTooltip> </Draggable>
<template #title> </div>
{{ $t('general.edit') }}
</template>
<NcButton
v-if="!source.is_meta && !source.is_local"
size="small"
class="nc-action-btn cursor-pointer outline-0 !w-8 !px-1 !rounded-lg"
type="text"
@click="baseAction(source.id, DataSourcesSubTab.Edit)"
>
<GeneralIcon icon="edit" class="text-gray-600" />
</NcButton>
</NcTooltip>
<NcTooltip>
<template #title>
{{ $t('general.remove') }}
</template>
<NcButton
v-if="!source.is_meta && !source.is_local"
size="small"
class="nc-action-btn cursor-pointer outline-0 !w-8 !px-1 !rounded-lg"
type="text"
@click="openDeleteBase(source)"
>
<GeneralIcon icon="delete" class="text-red-500" />
</NcButton>
</NcTooltip>
</div>
</div>
</template>
</Draggable>
</div>
</div>
<LazyDashboardSettingsDataSourcesCreateBase
v-model:open="isNewBaseModalOpen"
:connection-type="clientType"
@source-created="loadBases(true)"
/>
<GeneralModal v-model:visible="isErdModalOpen" size="large">
<div class="h-[80vh]">
<LazyDashboardSettingsErd :source-id="activeBaseId" :show-all-columns="false" />
</div>
</GeneralModal>
<GeneralModal v-model:visible="isMetaDataModal" size="medium">
<div class="p-6">
<LazyDashboardSettingsMetadata :source-id="activeBaseId" @source-synced="loadBases(true)" />
</div>
</GeneralModal>
<GeneralModal v-model:visible="isUIAclModalOpen" class="!w-[60rem]">
<div class="p-6">
<LazyDashboardSettingsUIAcl :source-id="activeBaseId" />
</div>
</GeneralModal>
<GeneralModal v-model:visible="isEditBaseModalOpen" closable :mask-closable="false" size="medium">
<div class="p-6">
<LazyDashboardSettingsDataSourcesEditBase
:source-id="activeBaseId"
@source-updated="loadBases(true)"
@close="isEditBaseModalOpen = false"
/>
</div>
</GeneralModal>
<GeneralModal v-model:visible="isBaseAuditModalOpen" class="!w-[70rem]">
<div class="p-6">
<LazyDashboardSettingsBaseAudit :source-id="activeBaseId" @close="isBaseAuditModalOpen = false" />
</div> </div>
</GeneralModal> <LazyDashboardSettingsDataSourcesCreateBase
<GeneralDeleteModal v-model:open="isNewBaseModalOpen"
v-model:visible="isDeleteBaseModalOpen" :connection-type="clientType"
:entity-name="$t('general.datasource')" @source-created="loadBases(true)"
:on-delete="deleteBase" />
:delete-label="$t('general.remove')" <GeneralDeleteModal
> v-model:visible="isDeleteBaseModalOpen"
<template #entity-preview> :entity-name="$t('general.datasource')"
<div v-if="toBeDeletedBase" class="flex flex-row items-center py-2 px-3.25 bg-gray-50 rounded-lg text-gray-700 mb-4"> :on-delete="deleteBase"
<GeneralBaseLogo :source-type="toBeDeletedBase.type" /> :delete-label="$t('general.remove')"
<div >
class="capitalize text-ellipsis overflow-hidden select-none w-full pl-3" <template #entity-preview>
:style="{ wordBreak: 'keep-all', whiteSpace: 'nowrap', display: 'inline' }" <div v-if="toBeDeletedBase" class="flex flex-row items-center py-2 px-3.25 bg-gray-50 rounded-lg text-gray-700 mb-4">
> <GeneralBaseLogo :source-type="toBeDeletedBase.type" />
{{ toBeDeletedBase.alias }} <div
class="capitalize text-ellipsis overflow-hidden select-none w-full pl-3"
:style="{ wordBreak: 'keep-all', whiteSpace: 'nowrap', display: 'inline' }"
>
{{ toBeDeletedBase.alias }}
</div>
</div> </div>
</div> </template>
</template> </GeneralDeleteModal>
</GeneralDeleteModal> </div>
</div> </div>
</div> </div>
</template> </template>
<style> <style scoped lang="scss">
.ds-table-head { .ds-table-head {
@apply flex items-center border-0 text-gray-500; @apply flex items-center border-0 text-gray-500;
} }
@ -656,7 +497,7 @@ const isEditBaseModalOpen = computed({
} }
.ds-table-row { .ds-table-row {
@apply grid grid-cols-20 border-b border-gray-100 w-full h-full; @apply grid grid-cols-18 border-b border-gray-100 w-full h-full;
} }
.ds-table-col { .ds-table-col {
@ -676,11 +517,7 @@ const isEditBaseModalOpen = computed({
} }
.ds-table-actions { .ds-table-actions {
@apply col-span-5 flex w-full justify-end; @apply col-span-5 flex w-full justify-center;
}
.ds-table-crud {
@apply col-span-2;
} }
.ds-table-col:last-child { .ds-table-col:last-child {
@ -690,4 +527,15 @@ const isEditBaseModalOpen = computed({
.ds-table-handle { .ds-table-handle {
@apply cursor-pointer justify-self-start mr-2 w-[16px]; @apply cursor-pointer justify-self-start mr-2 w-[16px];
} }
.ds-table-body .ds-table-row:hover {
@apply bg-gray-50/60;
}
:deep(.ant-tabs-content),
:deep(.ant-tabs) {
@apply !h-full;
}
:deep(.ant-tabs-content-holder) {
@apply !min-h-0 !flex-shrink;
}
</style> </style>

107
packages/nc-gui/components/dashboard/settings/Modal.vue

@ -1,6 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import type { FunctionalComponent, SVGAttributes } from 'vue' import type { FunctionalComponent, SVGAttributes } from 'vue'
import Misc from './Misc.vue' import Misc from './Misc.vue'
import DataSources from '~/components/dashboard/settings/DataSources.vue'
interface Props { interface Props {
modelValue?: boolean modelValue?: boolean
@ -39,6 +40,8 @@ const vDataState = useVModel(props, 'dataSourcesState', emits)
const baseId = toRef(props, 'baseId') const baseId = toRef(props, 'baseId')
const { isUIAllowed } = useRoles()
provide(ProjectIdInj, baseId) provide(ProjectIdInj, baseId)
const { $e } = useNuxtApp() const { $e } = useNuxtApp()
@ -77,21 +80,6 @@ const tabsInfo: TabGroup = {
// $e('c:settings:team-auth') // $e('c:settings:team-auth')
// }, // },
// }, // },
// dataSources: {
// // Data Sources
// title: 'Data Sources',
// icon: iconMap.datasource,
// subTabs: {
// dataSources: {
// title: 'Data Sources',
// body: DataSources,
// },
// },
// onClick: () => {
// vDataState.value = ''
// $e('c:settings:data-sources')
// },
// },
// audit: { // audit: {
// // Audit // // Audit
// title: t('title.audit'), // title: t('title.audit'),
@ -123,6 +111,22 @@ const tabsInfo: TabGroup = {
$e('c:settings:base-settings') $e('c:settings:base-settings')
}, },
}, },
dataSources: {
// Data Sources
title: 'Data Sources',
icon: iconMap.database,
subTabs: {
dataSources: {
title: 'Data Sources',
body: DataSources,
},
},
onClick: () => {
vDataState.value = ''
$e('c:settings:data-sources')
},
},
} }
const firstKeyOfObject = (obj: object) => Object.keys(obj)[0] const firstKeyOfObject = (obj: object) => Object.keys(obj)[0]
@ -154,6 +158,7 @@ watch(
:footer="null" :footer="null"
width="max(90vw, 600px)" width="max(90vw, 600px)"
:closable="false" :closable="false"
class="!top-50px !bottom-50px"
wrap-class-name="nc-modal-settings" wrap-class-name="nc-modal-settings"
@cancel="emits('update:modelValue', false)" @cancel="emits('update:modelValue', false)"
> >
@ -173,24 +178,30 @@ watch(
</a-button> </a-button>
</div> </div>
<a-layout class="mt-3 h-[75vh] overflow-y-auto flex"> <a-layout class="mt-3 overflow-y-auto flex">
<!-- Side tabs --> <!-- Side tabs -->
<a-layout-sider> <a-layout-sider>
<a-menu v-model:selected-keys="selectedTabKeys" class="tabs-menu h-full" :open-keys="[]"> <a-menu v-model:selected-keys="selectedTabKeys" class="tabs-menu h-full" :open-keys="[]">
<a-menu-item v-for="(tab, key) of tabsInfo" :key="key" class="active:(!ring-0) hover:(!bg-primary !bg-opacity-25)"> <template v-for="(tab, key) of tabsInfo" :key="key">
<div class="flex items-center space-x-2" @click="tab.onClick"> <a-menu-item
<component :is="tab.icon" /> v-if="key !== 'dataSources' || isUIAllowed('sourceCreate')"
:key="key"
<div class="select-none"> class="active:(!ring-0) hover:(!bg-primary !bg-opacity-25)"
{{ tab.title }} >
<div class="flex items-center space-x-2" @click="tab.onClick">
<component :is="tab.icon" />
<div class="select-none">
{{ tab.title }}
</div>
</div> </div>
</div> </a-menu-item>
</a-menu-item> </template>
</a-menu> </a-menu>
</a-layout-sider> </a-layout-sider>
<!-- Sub Tabs --> <!-- Sub Tabs -->
<a-layout-content class="h-auto px-4 scrollbar-thumb-gray-500"> <a-layout-content class="h-auto h-80vh px-4 scrollbar-thumb-gray-500">
<a-menu <a-menu
v-if="selectedTabKeys[0] !== 'dataSources'" v-if="selectedTabKeys[0] !== 'dataSources'"
v-model:selectedKeys="selectedSubTabKeys" v-model:selectedKeys="selectedSubTabKeys"
@ -206,51 +217,19 @@ watch(
{{ tab.title }} {{ tab.title }}
</a-menu-item> </a-menu-item>
</a-menu> </a-menu>
<div v-else>
<div class="flex items-center">
<a-breadcrumb class="w-full cursor-pointer">
<a-breadcrumb-item v-if="vDataState !== ''" @click="vDataState = ''">
<a class="!no-underline">Data Sources</a>
</a-breadcrumb-item>
<a-breadcrumb-item v-else @click="vDataState = ''">Data Sources</a-breadcrumb-item>
<a-breadcrumb-item v-if="vDataState !== ''">{{ vDataState }}</a-breadcrumb-item>
</a-breadcrumb>
<div v-if="vDataState === ''" class="flex flex-row justify-end items-center w-full gap-1">
<a-button
v-if="!isDataSourceLimitReached"
type="primary"
class="self-start !rounded-md nc-btn-new-datasource"
@click="vDataState = DataSourcesSubTab.New"
>
<div v-if="vDataState === ''" class="flex items-center gap-2 font-light">
<component :is="iconMap.plusCircle" class="group-hover:text-accent" />
New
</div>
</a-button>
<!-- Reload -->
<a-button
v-e="['a:proj-meta:data-sources:reload']"
type="text"
class="self-start !rounded-md nc-btn-metasync-reload"
@click="dataSourcesReload = true"
>
<div class="flex items-center gap-2 text-gray-600 font-light">
<component :is="iconMap.reload" :class="{ 'animate-infinite animate-spin !text-success': dataSourcesReload }" />
{{ $t('general.reload') }}
</div>
</a-button>
</div>
</div>
<a-divider style="margin: 10px 0" />
</div>
<div class="h-[600px]"> <div
class="overflow-auto"
:class="{
'h-full': selectedSubTabKeys[0] === 'dataSources',
}"
>
<component <component
:is="selectedSubTab?.body" :is="selectedSubTab?.body"
v-if="selectedSubTabKeys[0] === 'dataSources'" v-if="selectedSubTabKeys[0] === 'dataSources'"
v-model:state="vDataState" v-model:state="vDataState"
v-model:reload="dataSourcesReload" v-model:reload="dataSourcesReload"
class="px-2 pb-2" class="px-2 pb-2 h-full"
:data-testid="`nc-settings-subtab-${selectedSubTab.key}`" :data-testid="`nc-settings-subtab-${selectedSubTab.key}`"
:base-id="baseId" :base-id="baseId"
/> />

2
packages/nc-gui/components/dashboard/settings/UIAcl.vue

@ -131,7 +131,7 @@ const toggleSelectAll = (role: Role) => {
<template> <template>
<div class="flex flex-row w-full items-center justify-center"> <div class="flex flex-row w-full items-center justify-center">
<div class="flex flex-col w-[900px]"> <div class="flex flex-col">
<NcTooltip class="mb-4 first-letter:capital font-bold max-w-100 truncate" show-on-truncate-only> <NcTooltip class="mb-4 first-letter:capital font-bold max-w-100 truncate" show-on-truncate-only>
<template #title>{{ base.title }}</template> <template #title>{{ base.title }}</template>
<span> UI ACL : {{ base.title }} </span> <span> UI ACL : {{ base.title }} </span>

53
packages/nc-gui/components/dlg/ProjectAudit.vue

@ -0,0 +1,53 @@
<script lang="ts" setup>
const props = defineProps<{
baseId: string
sourceId: string
modelValue: boolean
}>()
const emit = defineEmits(['update:modelValue'])
const isOpen = useVModel(props, 'modelValue', emit)
const activeSourceId = computed(() => props.sourceId)
const { openedProject: base } = storeToRefs(useBases())
const { baseTables } = storeToRefs(useTablesStore())
const { loadProjectTables } = useTablesStore()
const isLoading = ref(true)
const { getMeta } = useMetas()
const baseId = computed(() => props.baseId || base.value?.id)
onMounted(async () => {
if (baseId.value && baseTables.value.get(baseId.value)) {
return (isLoading.value = false)
}
try {
await loadProjectTables(baseId.value!)
await Promise.all(
baseTables.value.get(baseId.value!)!.map(async (table) => {
await getMeta(table.id!, false, false, baseId.value!)
}),
)
} catch (e) {
message.error('Failed to load tables/bases. Please try again later.')
console.error(e)
} finally {
isLoading.value = false
}
})
</script>
<template>
<GeneralModal v-model:visible="isOpen" size="large" class="!w-[70rem]">
<div class="p-6">
<DashboardSettingsBaseAudit v-if="!isLoading" :source-id="activeSourceId" :base-id="baseId" :show-all-columns="false" />
</div>
</GeneralModal>
</template>

6
packages/nc-gui/components/project/AllTables.vue

@ -105,7 +105,7 @@ const onCreateBaseClick = () => {
<GeneralIcon icon="download" /> <GeneralIcon icon="download" />
<div class="label">{{ $t('activity.import') }} {{ $t('general.data') }}</div> <div class="label">{{ $t('activity.import') }} {{ $t('general.data') }}</div>
</div> </div>
<component :is="isDataSourceLimitReached ? NcTooltip : 'div'" v-if="isUIAllowed('sourceCreate')"> <!-- <component :is="isDataSourceLimitReached ? NcTooltip : 'div'" v-if="isUIAllowed('sourceCreate')">
<template #title> <template #title>
<div> <div>
{{ $t('tooltip.reachedSourceLimit') }} {{ $t('tooltip.reachedSourceLimit') }}
@ -124,7 +124,7 @@ const onCreateBaseClick = () => {
<GeneralIcon icon="dataSource" /> <GeneralIcon icon="dataSource" />
<div class="label">{{ $t('labels.connectDataSource') }}</div> <div class="label">{{ $t('labels.connectDataSource') }}</div>
</div> </div>
</component> </component>-->
</div> </div>
<div <div
v-if="base?.isLoading" v-if="base?.isLoading"
@ -191,7 +191,7 @@ const onCreateBaseClick = () => {
</div> </div>
<ProjectImportModal v-if="defaultBase" v-model:visible="isImportModalOpen" :source="defaultBase" /> <ProjectImportModal v-if="defaultBase" v-model:visible="isImportModalOpen" :source="defaultBase" />
<LazyDashboardSettingsDataSourcesCreateBase v-model:open="isNewBaseModalOpen" /> <!-- <LazyDashboardSettingsDataSourcesCreateBase v-model:open="isNewBaseModalOpen" />-->
</div> </div>
</template> </template>

4
packages/nc-gui/components/project/View.vue

@ -162,7 +162,7 @@ watch(
</template> </template>
<ProjectAccessSettings :base-id="currentBase?.id" /> <ProjectAccessSettings :base-id="currentBase?.id" />
</a-tab-pane> </a-tab-pane>
<a-tab-pane v-if="isUIAllowed('sourceCreate')" key="data-source"> <!-- <a-tab-pane v-if="isUIAllowed('sourceCreate')" key="data-source">
<template #tab> <template #tab>
<div class="tab-title" data-testid="proj-view-tab__data-sources"> <div class="tab-title" data-testid="proj-view-tab__data-sources">
<GeneralIcon icon="database" /> <GeneralIcon icon="database" />
@ -180,7 +180,7 @@ watch(
</div> </div>
</template> </template>
<DashboardSettingsDataSources v-model:state="baseSettingsState" /> <DashboardSettingsDataSources v-model:state="baseSettingsState" />
</a-tab-pane> </a-tab-pane>-->
</a-tabs> </a-tabs>
</div> </div>
</div> </div>

2
packages/nc-gui/lang/en.json

@ -448,6 +448,8 @@
"noResultsMatchedYourSearch": "Your search did not yield any matching results" "noResultsMatchedYourSearch": "Your search did not yield any matching results"
}, },
"labels": { "labels": {
"connectionDetails": "Connection Details",
"metaSync": "Meta Sync",
"today": "Today", "today": "Today",
"workspace": "Workspace", "workspace": "Workspace",
"txt": "TXT Record value", "txt": "TXT Record value",

8
packages/noco-docs/docs/100.data-sources/010.data-source-overview.md

@ -21,10 +21,12 @@ Currently only one external data source can be added per project.
## Accessing `Data Sources` ## Accessing `Data Sources`
1. Access Base context menu by clicking on the `Base` name in the left sidebar 1. Access Base context menu by clicking on the `...` in the left sidebar against the base name
2. Click on `Data Sources` tab 2. Click on `Settings` tab
3. In the popup modal, click on `Data Sources` tab
![data source](/img/v2/data-source/data-source.png) ![data source](/img/v2/data-source/data-source-1.png)
![data source](/img/v2/data-source/data-source-2.png)
Learn more about working with Data sources in the following sections: Learn more about working with Data sources in the following sections:

29
packages/noco-docs/docs/100.data-sources/020.connect-to-data-source.md

@ -7,10 +7,11 @@ keywords: ['NocoDB data source', 'connect data source', 'external data source',
To connect to an external data source, follow the steps below: To connect to an external data source, follow the steps below:
1. Access Base context menu by clicking on the `Base` name in the left sidebar 1. Access the Base context menu by clicking on the `...` in the left sidebar against the base name
2. Select `Data Sources` tab 2. Click on `Settings` tab
3. Click on `+ New Data Source` button 3. In the popup modal, click on `Data Sources` tab
4. On the pop-up modal, provide the following details: 4. Click on `+ New Data Source` button
5. On the input modal, provide the following details:
| Field Name | Description | | Field Name | Description |
|---------------|--------------------------------------------------------------------------------------| |---------------|--------------------------------------------------------------------------------------|
@ -23,7 +24,7 @@ To connect to an external data source, follow the steps below:
| Database | Name of the database to connect to | | Database | Name of the database to connect to |
| Schema name | Name of the schema to connect to | | Schema name | Name of the schema to connect to |
4a. Optionally, if the connection required is TLS/MTLS for MITM protection, follow these additional steps below: 5a. Optionally, if the connection required is TLS/MTLS for MITM protection, follow these additional steps below:
- Click on `SSL & Advanced Parameters` - Click on `SSL & Advanced Parameters`
- Select `SSL Mode` and upload the client certificate, client key, and Root CA files by clicking on the file. - Select `SSL Mode` and upload the client certificate, client key, and Root CA files by clicking on the file.
@ -33,16 +34,18 @@ To connect to an external data source, follow the steps below:
Example: In PostgreSQL when SSL mode set to "Required-Identity," if the server certificate's common name (cname) differs from the actual DNS/IP used for connection, the connection will fail.\ Example: In PostgreSQL when SSL mode set to "Required-Identity," if the server certificate's common name (cname) differs from the actual DNS/IP used for connection, the connection will fail.\
To resolve, add "servername" property with same cname value under the SSL section. Additional details are available at [knex configuration options](https://knexjs.org/guide/#configuration-options). To resolve, add "servername" property with same cname value under the SSL section. Additional details are available at [knex configuration options](https://knexjs.org/guide/#configuration-options).
5. Click on `Test Database Connection` button to verify the connection 6. Click on the `Test Database Connection` button to verify the connection
6. Wait for the connection to be verified. 7. Wait for the connection to be verified.
- After connection is successful, `Submit` button will be enabled. - After test is successful, `Counnect to Data Source` button will be enabled.
- Click on `Submit` button to save the data source. - Click on `Connect to Data Source` button to save the data source.
![data source-1](/img/v2/data-source/data-source-connect-1.png)
![data source-2](/img/v2/data-source/data-source-connect-2.png)
![data source-3](/img/v2/data-source/data-source-connect-3.png) ![data source-1](/img/v2/data-source/ds-connect-1.png)
![data source-4](/img/v2/data-source/data-source-connect-4a.png) ![data source-2](/img/v2/data-source/ds-connect-2.png)
![data source-3](/img/v2/data-source/ds-connect-3.png)
![data source-4](/img/v2/data-source/ds-connect-4.png)

18
packages/noco-docs/docs/100.data-sources/030.sync-with-data-source.md

@ -5,17 +5,15 @@ tags: ['Data Sources', 'Sync', 'External', 'PG', 'MySQL']
keywords: ['NocoDB data source', 'connect data source', 'external data source', 'PG data source', 'MySQL data source'] keywords: ['NocoDB data source', 'connect data source', 'external data source', 'PG data source', 'MySQL data source']
--- ---
Access `Data Sources` tab in the `Base Settings` to sync changes done in the external data source with NocoDB.
1. Access Base context menu by clicking on the `Base` name in the left sidebar 1. Select the data source that you wish to sync metadata for
2. Select `Data Sources` tab 2. Click on the `Meta Sync` button listed under `Actions` column for the data source that you wish to sync metadata for
3. Click on `Sync Metadata` button listed under `Actions` column for the data source that you wish to sync metadata for 3. Click on the `Reload` button to refresh Sync state (Optional)
4. Click on `Reload` button to refresh Sync state (Optional) 4. Any changes to the metadata identified will be listed in the `Sync State` column
5. Any changes to the metadata identified will be listed in the `Sync State` column 5. Click on `Sync Now` button to sync the metadata changes
6. Click on `Sync Now` button to sync the metadata changes
![sync metadata](/img/v2/data-source/data-source-2.png) ![sync metadata](/img/v2/data-source/data-source-meta-sync-1.png)
![sync metadata](/img/v2/data-source/data-source-meta-sync-2.png)
![sync metadata](/img/v2/data-source/data-source-meta-sync.png)
After the sync is complete, you can see the updated state in the `Sync State` column. After the sync is complete, you can see the updated state in the `Sync State` column.
Sync modal also marks `Tables metadata is in Sync` in the header. Sync modal also marks `Tables metadata is in Sync` in the header.

69
packages/noco-docs/docs/100.data-sources/040.actions-on-data-sources.md

@ -6,24 +6,22 @@ keywords: ['NocoDB data source', 'UI ACL', 'Audit logs', 'Relations', 'Edit', 'U
--- ---
## Edit external database configuration parameters ## Edit external database configuration parameters
- Access `Data Sources` tab in the `Base Settings`
- Click on `Connection Details` tab
- Re-configure database credentials as required
1. Access Base context menu by clicking on the `Base` name in the left sidebar :::info
2. Click on `Data Sources` tab Please make sure database configuration parameters are valid. Any incorrect parameters could lead to schema loss!
3. Click on `Edit` icon listed under `Actions` column for the data source that you wish to access ERD (Relations view) for :::
Go to `Data Sources`, click ``Edit`` icon, you can re-configure database credentials.
Please make sure database configuration parameters are valid. Any incorrect parameters could lead to schema loss!
![relations](/img/v2/data-source/data-source-edit.png)
![edit db config](/img/v2/data-source/edit-base.png) ![edit-data-source](/img/v2/data-source/data-source-edit.png)
## Remove data source ## Remove data source
1. Access Base context menu by clicking on the `Base` name in the left sidebar - Access `Data Sources` tab in the `Base Settings`
2. Click on `Data Sources` tab - Click on `Delete` icon listed under `Actions` column for the data source that you wish to remove
3. Click on `Delete` icon listed under `Actions` column for the data source that you wish to Unlink
![datasource unlink](/img/v2/data-source/data-source-unlink.png) ![datasource unlink](/img/v2/data-source/data-source-remove.png)
:::note :::note
Unlinking a data source will not delete the external data source. It will only remove the data source from the current project. Unlinking a data source will not delete the external data source. It will only remove the data source from the current project.
@ -32,52 +30,45 @@ Unlinking a data source will not delete the external data source. It will only r
## Data source visibility ## Data source visibility
1. Access Base context menu by clicking on the `Base` name in the left sidebar - Access `Data Sources` tab in the `Base Settings`
2. Click on `Data Sources` tab - Toggle radio button listed under `Visibility` column for the data source that you wish to hide/un-hide
3. Toggle radio button listed under `Visibility` column for the data source that you wish to hide/un-hide
![datasource visibility](/img/v2/data-source/data-source-visibility.png) ![datasource visibility](/img/v2/data-source/data-source-hide.png)
## UI Access Control ## UI Access Control
:::info :::info
UI Access Control is available only in Open Source version of NocoDB. UI Access Control is available only in Open-Source version of NocoDB.
::: :::
1. Access Base context menu by clicking on the `Base` name in the left sidebar Access `Data Sources` tab in the `Base Settings` to manage UI access control for the data source.
2. Click on `Data Sources` tab 1. Click on `UI ACL` button listed under `Actions` column for the data source that you wish to manage UI access control for
3. Click on `UI ACL` button listed under `Actions` column for the data source that you wish to manage UI access control for 2. On the UI ACL modal, you can see the list of tables available in the data source as rows & roles available as columns. Toggle checkboxes to enable/disable access to tables for specific roles.
4. On the UI ACL modal, you can see the list of tables available in the data source as rows & roles available as columns. Toggle checkboxes to enable/disable access to tables for specific roles. 3. Click on `Save` button to save the changes
5. Click on `Save` button to save the changes
![ui acl](/img/v2/data-source/data-source-3.png) ![ui acl](/img/v2/data-source/data-source-uiacl.png)
![ui acl](/img/v2/data-source/ui-acl.png)
## Audit logs ## Audit logs
1. Access Base context menu by clicking on the `Base` name in the left sidebar Access `Data Sources` tab in the `Base Settings` to access Audit logs for the data source.
2. Click on `Data Sources` tab - Click on `Default` datasource & then
3. Click on `Audit` button listed under `Actions` column for the data source that you wish to access Audit logs for - Access `Audit` tab to view the audit logs.
![audit](/img/v2/data-source/audit.png)
![audit logs](/img/v2/data-source/audit-logs.png) ![audit](/img/v2/data-source/data-source-audit.png)
:::info
Audit logs are not available for external data source connections.
:::
## Relations ## Relations
Access `Data Sources` tab in the `Base Settings` to access Relations view for the data source.
- Select the data source that you wish to access ERD (Relations view) for
- Click on `ERD` tab
1. Access Base context menu by clicking on the `Base` name in the left sidebar ![relations](/img/v2/data-source/data-source-erd.png)
2. Click on `Data Sources` tab
3. Click on `Relations` button listed under `Actions` column for the data source that you wish to access ERD (Relations view) for
![relations](/img/v2/data-source/data-source-4.png)
![relations](https://github.com/nocodb/nocodb/assets/86527202/c3775d27-f75d-4263-8903-dd66427de4b4)
### Junction table names within Relations ### Junction table names within Relations

BIN
packages/noco-docs/static/img/v2/data-source/data-source-1.png vendored

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 KiB

BIN
packages/noco-docs/static/img/v2/data-source/data-source-2.png vendored

Binary file not shown.

Before

Width:  |  Height:  |  Size: 175 KiB

After

Width:  |  Height:  |  Size: 128 KiB

BIN
packages/noco-docs/static/img/v2/data-source/data-source-audit.png vendored

Binary file not shown.

After

Width:  |  Height:  |  Size: 323 KiB

BIN
packages/noco-docs/static/img/v2/data-source/data-source-edit.png vendored

Binary file not shown.

Before

Width:  |  Height:  |  Size: 175 KiB

After

Width:  |  Height:  |  Size: 278 KiB

BIN
packages/noco-docs/static/img/v2/data-source/data-source-erd.png vendored

Binary file not shown.

After

Width:  |  Height:  |  Size: 403 KiB

BIN
packages/noco-docs/static/img/v2/data-source/data-source-hide.png vendored

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

BIN
packages/noco-docs/static/img/v2/data-source/data-source-meta-sync-1.png vendored

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

BIN
packages/noco-docs/static/img/v2/data-source/data-source-meta-sync-2.png vendored

Binary file not shown.

After

Width:  |  Height:  |  Size: 267 KiB

BIN
packages/noco-docs/static/img/v2/data-source/data-source-remove.png vendored

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

BIN
packages/noco-docs/static/img/v2/data-source/data-source-uiacl.png vendored

Binary file not shown.

After

Width:  |  Height:  |  Size: 330 KiB

BIN
packages/noco-docs/static/img/v2/data-source/ds-connect-1.png vendored

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 KiB

BIN
packages/noco-docs/static/img/v2/data-source/ds-connect-2.png vendored

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

BIN
packages/noco-docs/static/img/v2/data-source/ds-connect-3.png vendored

Binary file not shown.

After

Width:  |  Height:  |  Size: 268 KiB

BIN
packages/noco-docs/static/img/v2/data-source/ds-connect-4.png vendored

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

4
tests/playwright/pages/Dashboard/ProjectView/Metadata.ts

@ -25,7 +25,9 @@ export class MetaDataPage extends BasePage {
await this.get().click(); await this.get().click();
await this.rootPage.keyboard.press('Escape'); await this.rootPage.keyboard.press('Escape');
await this.rootPage.keyboard.press('Escape'); await this.rootPage.keyboard.press('Escape');
await this.get().waitFor({ state: 'detached' }); await this.rootPage.waitForSelector('div.ant-modal-content', {
state: 'hidden',
});
} }
async sync() { async sync() {

3
tests/playwright/pages/Dashboard/ProjectView/index.ts

@ -54,12 +54,9 @@ export class ProjectViewPage extends BasePage {
expect(await this.tab_allTables.isVisible()).toBeTruthy(); expect(await this.tab_allTables.isVisible()).toBeTruthy();
if (role.toLowerCase() === 'creator' || role.toLowerCase() === 'owner') { if (role.toLowerCase() === 'creator' || role.toLowerCase() === 'owner') {
await this.tab_dataSources.waitFor({ state: 'visible' });
await this.tab_accessSettings.waitFor({ state: 'visible' }); await this.tab_accessSettings.waitFor({ state: 'visible' });
expect(await this.tab_dataSources.isVisible()).toBeTruthy();
expect(await this.tab_accessSettings.isVisible()).toBeTruthy(); expect(await this.tab_accessSettings.isVisible()).toBeTruthy();
} else { } else {
expect(await this.tab_dataSources.isVisible()).toBeFalsy();
expect(await this.tab_accessSettings.isVisible()).toBeFalsy(); expect(await this.tab_accessSettings.isVisible()).toBeFalsy();
} }

30
tests/playwright/pages/Dashboard/Settings/DataSources.ts

@ -20,21 +20,35 @@ export class DataSourcesPage extends BasePage {
} }
get() { get() {
return this.settings.get().locator(`[data-testid="nc-settings-subtab-Data Sources"]`); return this.settings.get().locator('[data-testid="nc-settings-datasources"]');
} }
async openErd({ dataSourceName }: { dataSourceName: string }) { async openErd({ rowIndex }: { rowIndex: number }) {
await this.get().locator('.ds-table-row', { hasText: dataSourceName }).locator('button:has-text("ERD")').click(); const row = this.get()
.locator('.ds-table-row')
.nth(rowIndex + 1);
await row.click();
await this.get().getByTestId('nc-erd-tab').click();
}
async openAudit({ rowIndex }: { rowIndex: number }) {
const row = this.get()
.locator('.ds-table-row')
.nth(rowIndex + 1);
await row.click();
await this.get().getByTestId('nc-audit-tab').click();
} }
async openAcl({ dataSourceName = defaultBaseName }: { dataSourceName?: string } = {}) { async openAcl({ dataSourceName = defaultBaseName }: { dataSourceName?: string } = {}) {
await this.get().locator('.ds-table-row', { hasText: dataSourceName }).locator('button:has-text("UI ACL")').click(); await this.get().locator('.ds-table-row', { hasText: dataSourceName }).locator('button:has-text("UI ACL")').click();
} }
async openMetaSync({ dataSourceName = defaultBaseName }: { dataSourceName?: string } = {}) { async openMetaSync({ rowIndex }: { rowIndex: number }) {
await this.get() // 0th offset for header
.locator('.ds-table-row', { hasText: dataSourceName }) const row = this.get()
.locator('button:has-text("Sync Metadata")') .locator('.ds-table-row')
.click(); .nth(rowIndex + 1);
await row.click();
await this.get().getByTestId('nc-meta-sync-tab').click();
} }
} }

4
tests/playwright/pages/Dashboard/TreeView.ts

@ -182,7 +182,7 @@ export class TreeViewPage extends BasePage {
await this.waitForTableOptions({ title }); await this.waitForTableOptions({ title });
await this.get().locator(`.nc-base-tree-tbl-${tableTitle}`).locator('.nc-tbl-context-menu').click(); await this.get().locator(`.nc-base-tree-tbl-${tableTitle}`).locator('.nc-tbl-context-menu').click();
await this.rootPage.locator('.ant-dropdown').locator('.nc-menu-item:has-text("Delete")').click(); await this.rootPage.locator('.ant-dropdown').locator('.nc-menu-item.nc-table-delete:has-text("Delete")').click();
await this.waitForResponse({ await this.waitForResponse({
uiAction: async () => { uiAction: async () => {
@ -205,7 +205,7 @@ export class TreeViewPage extends BasePage {
await this.waitForTableOptions({ title }); await this.waitForTableOptions({ title });
await this.get().locator(`.nc-base-tree-tbl-${tableTitle}`).locator('.nc-tbl-context-menu').click(); await this.get().locator(`.nc-base-tree-tbl-${tableTitle}`).locator('.nc-tbl-context-menu').click();
await this.rootPage.locator('.ant-dropdown').locator('.nc-menu-item:has-text("Rename")').click(); await this.rootPage.locator('.ant-dropdown').locator('.nc-table-rename.nc-menu-item:has-text("Rename")').click();
await this.dashboard.get().locator('[placeholder="Enter table name"]').fill(newTitle); await this.dashboard.get().locator('[placeholder="Enter table name"]').fill(newTitle);
await this.dashboard.get().locator('button:has-text("Rename Table")').click(); await this.dashboard.get().locator('button:has-text("Rename Table")').click();

6
tests/playwright/tests/db/features/erd.spec.ts

@ -45,8 +45,10 @@ test.describe('Erd', () => {
}; };
const openProjectErd = async () => { const openProjectErd = async () => {
await dashboard.baseView.tab_dataSources.click(); await dashboard.treeView.baseSettings({ title: context.base.title });
await dashboard.baseView.dataSources.openERD({ rowIndex: 0 }); await dashboard.settings.selectTab({ tab: 'dataSources' });
await dashboard.settings.dataSources.openErd({ rowIndex: 0 });
// await dashboard.baseView.dataSources.openERD({ rowIndex: 0 });
}; };
const openErdOfATable = async (tableName: string) => { const openErdOfATable = async (tableName: string) => {

13
tests/playwright/tests/db/features/metaSync.spec.ts

@ -36,8 +36,12 @@ test.describe('Meta sync', () => {
test('Meta sync', async () => { test('Meta sync', async () => {
test.setTimeout(process.env.CI ? 100000 : 70000); test.setTimeout(process.env.CI ? 100000 : 70000);
await dashboard.baseView.tab_dataSources.click(); // await dashboard.baseView.tab_dataSources.click();
await dashboard.baseView.dataSources.openMetaSync({ rowIndex: 0 }); // await dashboard.baseView.dataSources.openMetaSync({ rowIndex: 0 });
await dashboard.treeView.baseSettings({ title: context.base.title });
await dashboard.settings.selectTab({ tab: 'dataSources' });
await dashboard.settings.dataSources.openMetaSync({ rowIndex: 0 });
await dbExec(`CREATE TABLE table1 (id INT NOT NULL, col1 INT NULL, PRIMARY KEY (id))`); await dbExec(`CREATE TABLE table1 (id INT NOT NULL, col1 INT NULL, PRIMARY KEY (id))`);
await dbExec(`CREATE TABLE table2 (id INT NOT NULL, col1 INT NULL, PRIMARY KEY (id))`); await dbExec(`CREATE TABLE table2 (id INT NOT NULL, col1 INT NULL, PRIMARY KEY (id))`);
@ -247,8 +251,9 @@ test.describe('Meta sync', () => {
`INSERT INTO table1 (id, col1, col2, col3, col4) VALUES (1,1,1,1,1), (2,2,2,2,2), (3,3,3,3,3), (4,4,4,4,4), (5,5,5,5,5), (6,6,6,6,6), (7,7,7,7,7), (8,8,8,8,8), (9,9,9,9,9);` `INSERT INTO table1 (id, col1, col2, col3, col4) VALUES (1,1,1,1,1), (2,2,2,2,2), (3,3,3,3,3), (4,4,4,4,4), (5,5,5,5,5), (6,6,6,6,6), (7,7,7,7,7), (8,8,8,8,8), (9,9,9,9,9);`
); );
await dashboard.baseView.tab_dataSources.click(); await dashboard.treeView.baseSettings({ title: context.base.title });
await dashboard.baseView.dataSources.openMetaSync({ rowIndex: 0 }); await dashboard.settings.selectTab({ tab: 'dataSources' });
await dashboard.settings.dataSources.openMetaSync({ rowIndex: 0 });
await metaData.clickReload(); await metaData.clickReload();
await metaData.sync(); await metaData.sync();

6
tests/playwright/tests/db/general/tableOperations.spec.ts

@ -32,8 +32,10 @@ test.describe('Table Operations', () => {
// Audit logs in clickhouse; locally wont be accessible // Audit logs in clickhouse; locally wont be accessible
await dashboard.treeView.openProject({ title: context.base.title, context }); await dashboard.treeView.openProject({ title: context.base.title, context });
await dashboard.baseView.tab_dataSources.click();
await dashboard.baseView.dataSources.openAudit({ rowIndex: 0 }); await dashboard.treeView.baseSettings({ title: context.base.title });
await dashboard.settings.selectTab({ tab: 'dataSources' });
await dashboard.settings.dataSources.openAudit({ rowIndex: 0 });
await audit.verifyRow({ await audit.verifyRow({
index: 0, index: 0,

Loading…
Cancel
Save