diff --git a/.github/workflows/playwright-test-workflow.yml b/.github/workflows/playwright-test-workflow.yml index 60b62f90ff..6705017965 100644 --- a/.github/workflows/playwright-test-workflow.yml +++ b/.github/workflows/playwright-test-workflow.yml @@ -13,9 +13,21 @@ on: jobs: playwright: - runs-on: [self-hosted, v2] - timeout-minutes: 100 + runs-on: ubuntu-20.04 + timeout-minutes: 40 steps: + # Reference: https://github.com/pierotofy/set-swap-space/blob/master/action.yml + - name: Set 5gb swap + shell: bash + # Delete the swap file, allocate a new one, and activate it + run: | + export SWAP_FILE=$(swapon --show=NAME | tail -n 1) + sudo swapoff $SWAP_FILE + sudo rm $SWAP_FILE + sudo fallocate -l 5G $SWAP_FILE + sudo chmod 600 $SWAP_FILE + sudo mkswap $SWAP_FILE + sudo swapon $SWAP_FILE - name: Setup Node uses: actions/setup-node@v3 with: @@ -30,31 +42,11 @@ jobs: with: # npm cache files are stored in `~/.npm` on Linux/macOS path: ~/.npm - key: ${{ runner.os }}-v2-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }} + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }} restore-keys: | - ${{ runner.os }}-v2-build-${{ env.cache-name }}- - ${{ runner.os }}-v2-build- - ${{ runner.os }}-v2 - - name: setup pg - if: ${{ inputs.db == 'pg' || ( inputs.db == 'sqlite' && inputs.shard == '1' ) }} - working-directory: ./ - run: | - service postgresql start - cd /var/lib/postgresql/ && sudo -u postgres psql -c "SELECT 'dropdb '||datname||'' FROM pg_database WHERE datistemplate = false AND datallowconn = true And datname NOT IN ('postgres')" |grep ' dropdb ' | sudo -u postgres /bin/bash ; cd - sudo -u postgres psql -c "ALTER USER postgres WITH PASSWORD 'password';" - sudo -u postgres psql -c "ALTER USER postgres WITH SUPERUSER;" - service postgresql restart - - name: Set CI env - run: export CI=true - - name: Kill stale servers - run: | - # export NODE_OPTIONS=\"--max_old_space_size=16384\"; - kill -9 $(lsof -t -i:8080) || echo "no process running on 8080" - kill -9 $(lsof -t -i:3000) || echo "no process running on 3000" - - name: Set CI env - run: export CI=true - - name: Set NC Edition - run: export EE=true + ${{ runner.os }}-build-${{ env.cache-name }}- + ${{ runner.os }}-build- + ${{ runner.os }}- - name: install dependencies nocodb-sdk working-directory: ./packages/nocodb-sdk run: npm install @@ -63,23 +55,16 @@ jobs: run: npm run build - name: Setup mysql if: ${{ inputs.db == 'mysql' }} - working-directory: ./packages/nocodb/tests/mysql-sakila-db - run: | - # Get a list of non-system databases and construct the DROP DATABASE statement for each - service mysql start - mysql -u'root' -p'password' -e "SHOW DATABASES" --skip-column-names | grep -Ev "(information_schema|mysql|performance_schema|sys)" | while read db; do - mysql -u'root' -p'password' -e "DROP DATABASE IF EXISTS \`$db\`"; - done - # keep sql_mode default except remove "STRICT_TRANS_TABLES" - mysql -u'root' -p'password' -e "SET GLOBAL sql_mode = 'ONLY_FULL_GROUP_BY,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION';" - # this is only needed for connecting to sakila db as its refeferred in multiple places in test code - mysql -u'root' -p'password' < 01-mysql-sakila-schema.sql - mysql -u'root' -p'password' < 02-mysql-sakila-insert-data.sql - - name: Setup pg for quick tests + working-directory: ./ + run: docker-compose -f ./tests/playwright/scripts/docker-compose-mysql-playwright.yml up -d & + - name: setup pg + if: ${{ inputs.db == 'pg' }} + working-directory: ./ + run: docker-compose -f ./tests/playwright/scripts/docker-compose-playwright-pg.yml up -d & + - name: setup pg for quick tests if: ${{ inputs.db == 'sqlite' && inputs.shard == '1' }} - working-directory: ./packages/nocodb/tests/pg-cy-quick/ - run: | - sudo -u postgres psql -U postgres -f 01-cy-quick.sql + working-directory: ./ + run: docker-compose -f ./tests/playwright/scripts/docker-compose-pg-pw-quick.yml up -d & - name: run frontend working-directory: ./packages/nc-gui run: npm run ci:run @@ -89,28 +74,26 @@ jobs: working-directory: ./packages/nocodb run: | npm install - npm run watch:run:playwright &> ${{ inputs.db }}_${{ inputs.shard }}_test_backend.log & + npm run watch:run:playwright > ${{ inputs.db }}_${{ inputs.shard }}_test_backend.log & - name: Run backend:mysql if: ${{ inputs.db == 'mysql' }} working-directory: ./packages/nocodb run: | npm install - npm run watch:run:playwright:mysql &> ${{ inputs.db }}_${{ inputs.shard }}_test_backend.log & + npm run watch:run:playwright:mysql > ${{ inputs.db }}_${{ inputs.shard }}_test_backend.log & - name: Run backend:pg if: ${{ inputs.db == 'pg' }} working-directory: ./packages/nocodb run: | npm install - npm run watch:run:playwright:pg &> ${{ inputs.db }}_${{ inputs.shard }}_test_backend.log & + npm run watch:run:playwright:pg > ${{ inputs.db }}_${{ inputs.shard }}_test_backend.log & - name: Cache playwright npm modules uses: actions/cache@v3 id: playwright-cache with: path: | **/tests/playwright/node_modules - key: cache-v2-nc-playwright-${{ hashFiles('**/tests/playwright/package-lock.json') }} - restore-keys: | - cache-v2-nc-playwright- + key: cache-nc-playwright-${{ hashFiles('**/tests/playwright/package-lock.json') }} - name: Install dependencies if: steps.playwright-cache.outputs.cache-hit != 'true' working-directory: ./tests/playwright @@ -137,20 +120,20 @@ jobs: working-directory: ./tests/playwright run: E2E_DB_TYPE=${{ inputs.db }} node ./scripts/stressTestNewlyAddedTest.js -# # Quick tests (pg on sqlite shard 0 and sqlite on sqlite shard 1) + # Quick tests (pg on sqlite shard 0 and sqlite on sqlite shard 1) # - name: Run quick server and tests (pg) # if: ${{ inputs.db == 'sqlite' && inputs.shard == '1' }} # working-directory: ./packages/nocodb # run: | # kill -9 $(lsof -t -i:8080) -# npm run watch:run:playwright:pg:cyquick > quick_${{ inputs.shard }}_test_backend.log & +# npm run watch:run:playwright:pg:cyquick & # - name: Run quick server and tests (sqlite) # if: ${{ inputs.db == 'sqlite' && inputs.shard == '2' }} # working-directory: ./packages/nocodb # run: | # kill -9 $(lsof -t -i:8080) # npm run watch:run:playwright:quick > quick_${{ inputs.shard }}_test_backend.log & -# - name: Wait for backend for sqlite-tests +# - name: Wait for backend & run quick tests # if: ${{ inputs.db == 'sqlite' }} # working-directory: ./tests/playwright # run: | @@ -158,11 +141,7 @@ jobs: # printf '.' # sleep 2 # done -# timeout-minutes: 1 -# - name: Run quick tests -# if: ${{ inputs.db == 'sqlite' }} -# working-directory: ./tests/playwright -# run: PLAYWRIGHT_HTML_REPORT=playwright-report-quick npm run test:quick +# PLAYWRIGHT_HTML_REPORT=playwright-report-quick npm run test:quick - uses: actions/upload-artifact@v3 if: ${{ inputs.db == 'sqlite' }} with: @@ -194,9 +173,3 @@ jobs: name: backend-logs-${{ inputs.db }}-${{ inputs.shard }} path: ./packages/nocodb/${{ inputs.db }}_${{ inputs.shard }}_test_backend.log retention-days: 2 - - name: stop database servers - if: always() - working-directory: ./packages/nocodb - run: | - service postgresql stop - service mysql stop \ No newline at end of file diff --git a/packages/nc-gui/components.d.ts b/packages/nc-gui/components.d.ts index 3b9a2c397e..d0e6ae0466 100644 --- a/packages/nc-gui/components.d.ts +++ b/packages/nc-gui/components.d.ts @@ -151,6 +151,7 @@ declare module '@vue/runtime-core' { MdiDotsVertical: typeof import('~icons/mdi/dots-vertical')['default'] MdiEarth: typeof import('~icons/mdi/earth')['default'] MdiEditOutline: typeof import('~icons/mdi/edit-outline')['default'] + MdiEye: typeof import('~icons/mdi/eye')['default'] MdiFlag: typeof import('~icons/mdi/flag')['default'] MdiFolder: typeof import('~icons/mdi/folder')['default'] MdiHeart: typeof import('~icons/mdi/heart')['default'] diff --git a/packages/nc-gui/components/cell/TextArea.vue b/packages/nc-gui/components/cell/TextArea.vue index ed9d8d1264..81782d1bd7 100644 --- a/packages/nc-gui/components/cell/TextArea.vue +++ b/packages/nc-gui/components/cell/TextArea.vue @@ -103,7 +103,7 @@ onClickOutside(inputWrapperRef, (e) => { + +
+
{{ t('msg.info.dbConnected') }}
+
+ {{ $t('general.cancel') }} + Ok & Add Base +
+
+

New Base @@ -656,6 +658,17 @@ watch( > + + + +
+
{{ t('msg.info.dbConnected') }}
+
+ {{ $t('general.cancel') }} + Ok & Add Base +
+
+

diff --git a/packages/nc-gui/components/dlg/ProjectDelete.vue b/packages/nc-gui/components/dlg/ProjectDelete.vue index a6fcd4612f..ae729f2a9e 100644 --- a/packages/nc-gui/components/dlg/ProjectDelete.vue +++ b/packages/nc-gui/components/dlg/ProjectDelete.vue @@ -1,4 +1,6 @@ diff --git a/packages/nc-gui/store/project.ts b/packages/nc-gui/store/project.ts index 7101b1a6a3..e9c572f20b 100644 --- a/packages/nc-gui/store/project.ts +++ b/packages/nc-gui/store/project.ts @@ -256,6 +256,19 @@ export const useProject = defineStore('projectStore', () => { }, ) + const navigateToProjectPage = async ({ page }: { page: 'all-table' | 'collaborator' | 'data-source' }) => { + await router.push({ + name: 'index-typeOrId-projectId-index-index', + params: { + typeOrId: route.value.params.typeOrId, + projectId: route.value.params.projectId, + }, + query: { + page, + }, + }) + } + return { project, bases, @@ -284,6 +297,7 @@ export const useProject = defineStore('projectStore', () => { setProject, projectUrl, getBaseType, + navigateToProjectPage, } }) diff --git a/packages/nc-gui/store/projects.ts b/packages/nc-gui/store/projects.ts index 9af558dd70..dbae9bd6f1 100644 --- a/packages/nc-gui/store/projects.ts +++ b/packages/nc-gui/store/projects.ts @@ -86,6 +86,10 @@ export const useProjects = defineStore('projectsStore', () => { await api.auth.projectUserUpdate(projectId, user.id, user as ProjectUserReqType) } + const removeProjectUser = async (projectId: string, user: User) => { + await api.auth.projectUserRemove(projectId, user.id) + } + const loadProjects = async (page: 'recent' | 'shared' | 'starred' | 'workspace' = 'recent') => { // if shared base then get the shared project and create a list if (route.value.params.typeOrId === 'base' && route.value.params.projectId) { @@ -102,7 +106,7 @@ export const useProjects = defineStore('projectsStore', () => { projects.value.set(project.id!, { ...(projects.value.get(project.id!) || {}), ...project, - bases: [...(projects.value.get(project.id!)?.bases ?? []), ...(project.bases ?? [])], + bases: [...(project.bases ?? projects.value.get(project.id!)?.bases ?? [])], isExpanded: route.value.params.projectId === project.id || projects.value.get(project.id!)?.isExpanded, isLoading: false, }) @@ -121,18 +125,9 @@ export const useProjects = defineStore('projectsStore', () => { isProjectsLoading.value = true try { - const { list } = await $api.project.list( - page - ? { - query: { - [page]: true, - }, - baseURL: getBaseUrl(activeWorkspace?.id ?? workspace?.id), - } - : { - baseURL: getBaseUrl(activeWorkspace?.id ?? workspace?.id), - }, - ) + const { list } = await $api.project.list({ + baseURL: getBaseUrl(activeWorkspace?.id ?? workspace?.id), + }) _projects = list projects.value = _projects.reduce((acc, project) => { @@ -176,7 +171,13 @@ export const useProjects = defineStore('projectsStore', () => { if (!force && isProjectPopulated(projectId)) return projects.value.get(projectId) const _project = await api.project.read(projectId) - _project.meta = typeof _project.meta === 'string' ? JSON.parse(_project.meta) : {} + + if (!_project) { + await navigateTo(`/`) + return + } + + _project.meta = _project?.meta && typeof _project.meta === 'string' ? JSON.parse(_project.meta) : {} const existingProject = projects.value.get(projectId) ?? ({} as any) @@ -224,7 +225,7 @@ export const useProjects = defineStore('projectsStore', () => { linked_db_project_ids: projectPayload.linkedDbProjectIds, }, { - baseURL: getBaseUrl('default'), + baseURL: getBaseUrl('nc'), }, ) @@ -286,6 +287,12 @@ export const useProjects = defineStore('projectsStore', () => { loadProject(activeProjectId.value) }) + const navigateToFirstProjectOrHome = async () => { + // if active project id is deleted, navigate to first project or home page + if (projectsList.value?.length) await navigateToProject({ projectId: projectsList.value[0].id! }) + else navigateTo('/') + } + return { projects, projectsList, @@ -310,6 +317,8 @@ export const useProjects = defineStore('projectsStore', () => { createProjectUser, updateProjectUser, navigateToProject, + removeProjectUser, + navigateToFirstProjectOrHome, } }) diff --git a/packages/nc-gui/store/tables.ts b/packages/nc-gui/store/tables.ts index e5a2125795..bddaefc7bf 100644 --- a/packages/nc-gui/store/tables.ts +++ b/packages/nc-gui/store/tables.ts @@ -129,7 +129,7 @@ export const useTablesStore = defineStore('tablesStore', () => { await getMeta(table.id as string) - const typeOrId = (route.value.params.typeOrId as string) || 'nc' + // const typeOrId = (route.value.params.typeOrId as string) || 'nc' let workspaceIdOrType = workspaceId @@ -144,7 +144,7 @@ export const useTablesStore = defineStore('tablesStore', () => { } await navigateTo({ - path: `/${workspaceIdOrType}/${projectIdOrBaseId}/table/${table?.id}${table.title ? `/${table.title}` : ''}`, + path: `/${workspaceIdOrType}/${projectIdOrBaseId}/table/${table?.id}`, query: route.value.query, }) } diff --git a/packages/nc-gui/store/views.ts b/packages/nc-gui/store/views.ts index 25faafe344..48ce6cff66 100644 --- a/packages/nc-gui/store/views.ts +++ b/packages/nc-gui/store/views.ts @@ -15,8 +15,8 @@ export const useViewsStore = defineStore('viewsStore', () => { const { activeTable } = storeToRefs(useTablesStore()) - const activeViewTitle = computed(() => { - if (!route.value.params.viewTitle?.length) return views.value.length ? views.value[0].title : undefined + const activeViewTitleOrId = computed(() => { + if (!route.value.params.viewTitle?.length) return views.value.length ? views.value[0].id : undefined return route.value.params.viewTitle }) @@ -42,9 +42,12 @@ export const useViewsStore = defineStore('viewsStore', () => { if (!activeTable.value) return undefined - if (!activeViewTitle.value) return undefined + if (!activeViewTitleOrId.value) return undefined - return views.value.find((v) => v.title === activeViewTitle.value) + return ( + views.value.find((v) => v.id === activeViewTitleOrId.value) ?? + views.value.find((v) => v.title === activeViewTitleOrId.value) + ) }, set(_view: ViewType | undefined) { if (sharedView.value) { @@ -55,7 +58,9 @@ export const useViewsStore = defineStore('viewsStore', () => { if (!activeTable.value) return if (!_view) return - const viewIndex = views.value.findIndex((v) => v.title === activeViewTitle.value) + const viewIndex = + views.value.findIndex((v) => v.id === activeViewTitleOrId.value) ?? + views.value.findIndex((v) => v.title === activeViewTitleOrId.value) if (viewIndex === -1) return views.value[viewIndex] = _view @@ -86,7 +91,7 @@ export const useViewsStore = defineStore('viewsStore', () => { projectId: route.value.params.projectId, type: route.value.params.type, viewId: route.value.params.viewId, - viewTitle: activeViewTitle.value, + viewTitle: activeViewTitleOrId.value, slugs: [page], }, }) @@ -124,6 +129,7 @@ export const useViewsStore = defineStore('viewsStore', () => { activeView, openedViewsTab, onViewsTabChange, + sharedView, } }) diff --git a/packages/nc-gui/store/webhooks.ts b/packages/nc-gui/store/webhooks.ts index 99789ee9ca..b22993a4f4 100644 --- a/packages/nc-gui/store/webhooks.ts +++ b/packages/nc-gui/store/webhooks.ts @@ -178,7 +178,7 @@ export const useWebhooksStore = defineStore('webhooksStore', () => { projectId: route.value.params.projectId, type: route.value.params.type, viewId: route.value.params.viewId, - viewTitle: activeView.title, + viewTitle: activeView.id, slugs: openMainPage ? ['webhook'] : ['webhook', openCreatePage ? 'create' : hookId!], }, } diff --git a/packages/nc-gui/utils/browserUtils.ts b/packages/nc-gui/utils/browserUtils.ts index 2c86fa9063..fa2a882ed6 100644 --- a/packages/nc-gui/utils/browserUtils.ts +++ b/packages/nc-gui/utils/browserUtils.ts @@ -2,4 +2,5 @@ export const isMac = () => /Mac/i.test(navigator.platform) export const isDrawerExist = () => document.querySelector('.ant-drawer-open') export const isDrawerOrModalExist = () => document.querySelector('.ant-modal.active, .ant-drawer-open') +export const isExpandedCellInputExist = () => document.querySelector('.expanded-cell-input') export const cmdKActive = () => document.querySelector('.cmdk-modal-active') diff --git a/packages/nc-gui/utils/virtualCell.ts b/packages/nc-gui/utils/virtualCell.ts index 77d224655f..169f25c129 100644 --- a/packages/nc-gui/utils/virtualCell.ts +++ b/packages/nc-gui/utils/virtualCell.ts @@ -1,7 +1,10 @@ import type { ColumnType, LinkToAnotherRecordType } from 'nocodb-sdk' import { RelationTypes, UITypes, isLinksOrLTAR } from 'nocodb-sdk' -export const isLTAR = (uidt: string, colOptions: unknown): colOptions is LinkToAnotherRecordType => isLinksOrLTAR(uidt) +export const isLTAR = (uidt: string | undefined, colOptions: unknown): colOptions is LinkToAnotherRecordType => { + if (!uidt) return false + return isLinksOrLTAR(uidt) +} export const isHm = (column: ColumnType) => isLTAR(column.uidt!, column.colOptions) && column.colOptions.type === RelationTypes.HAS_MANY diff --git a/packages/nocodb/src/middlewares/extract-ids/extract-ids.middleware.ts b/packages/nocodb/src/middlewares/extract-ids/extract-ids.middleware.ts index bbd807c7e6..6559d4ff44 100644 --- a/packages/nocodb/src/middlewares/extract-ids/extract-ids.middleware.ts +++ b/packages/nocodb/src/middlewares/extract-ids/extract-ids.middleware.ts @@ -154,11 +154,14 @@ export class ExtractIdsMiddleware implements NestMiddleware, CanActivate { }); req.ncProjectId = model?.project_id; } - // extract project id from query params only if it's userMe endpoint + // extract project id from query params only if it's userMe endpoint or webhook plugin list else if ( - ['/auth/user/me', '/api/v1/db/auth/user/me', '/api/v1/auth/user/me'].some( - (userMePath) => req.route.path === userMePath, - ) && + [ + '/auth/user/me', + '/api/v1/db/auth/user/me', + '/api/v1/auth/user/me', + '/api/v1/db/meta/plugins/webhook', + ].some((userMePath) => req.route.path === userMePath) && req.query.project_id ) { req.ncProjectId = req.query.project_id; diff --git a/packages/nocodb/src/modules/jobs/jobs/export-import/duplicate.processor.ts b/packages/nocodb/src/modules/jobs/jobs/export-import/duplicate.processor.ts index 587d32c5b8..e8d2333fe7 100644 --- a/packages/nocodb/src/modules/jobs/jobs/export-import/duplicate.processor.ts +++ b/packages/nocodb/src/modules/jobs/jobs/export-import/duplicate.processor.ts @@ -56,6 +56,7 @@ export class DuplicateProcessor { modelIds: models.map((m) => m.id), excludeViews, excludeHooks, + excludeData, }); elapsedTime( @@ -150,6 +151,7 @@ export class DuplicateProcessor { modelIds: [modelId], excludeViews, excludeHooks, + excludeData, }) )[0]; diff --git a/packages/nocodb/src/modules/jobs/jobs/export-import/export.service.ts b/packages/nocodb/src/modules/jobs/jobs/export-import/export.service.ts index 53ec6a90b0..d0c6814070 100644 --- a/packages/nocodb/src/modules/jobs/jobs/export-import/export.service.ts +++ b/packages/nocodb/src/modules/jobs/jobs/export-import/export.service.ts @@ -23,9 +23,11 @@ export class ExportService { modelIds: string[]; excludeViews?: boolean; excludeHooks?: boolean; + excludeData?: boolean; }) { const { modelIds } = param; + const excludeData = param?.excludeData || false; const excludeViews = param?.excludeViews || false; const excludeHooks = param?.excludeHooks || false; @@ -41,6 +43,8 @@ export class ExportService { for (const modelId of modelIds) { const model = await Model.get(modelId); + let pgSerialLastVal; + if (!model) return NcError.badRequest(`Model not found for id '${modelId}'`); @@ -67,6 +71,35 @@ export class ExportService { for (const column of model.columns) { await column.getColOptions(); + + // if data is not excluded, get currval for ai column (pg) + if (!excludeData) { + if (base.type === 'pg') { + if (column.ai) { + try { + const sqlClient = await NcConnectionMgrv2.getSqlClient(base); + const seq = await sqlClient.knex.raw( + `SELECT pg_get_serial_sequence('??', ?) as seq;`, + [model.table_name, column.column_name], + ); + if (seq.rows.length > 0) { + const seqName = seq.rows[0].seq; + + const res = await sqlClient.knex.raw( + `SELECT last_value as last FROM ${seqName};`, + ); + + if (res.rows.length > 0) { + pgSerialLastVal = res.rows[0].last; + } + } + } catch (e) { + this.logger.error(e); + } + } + } + } + if (column.colOptions) { for (const [k, v] of Object.entries(column.colOptions)) { switch (k) { @@ -235,6 +268,7 @@ export class ExportService { prefix: project.prefix, title: model.title, table_name: clearPrefix(model.table_name, project.prefix), + pgSerialLastVal, meta: model.meta, columns: model.columns.map((column) => ({ id: idMap.get(column.id), diff --git a/packages/nocodb/src/modules/jobs/jobs/export-import/import.service.ts b/packages/nocodb/src/modules/jobs/jobs/export-import/import.service.ts index b68bd5dad9..86b3733dcf 100644 --- a/packages/nocodb/src/modules/jobs/jobs/export-import/import.service.ts +++ b/packages/nocodb/src/modules/jobs/jobs/export-import/import.service.ts @@ -32,6 +32,7 @@ import { HooksService } from '~/services/hooks.service'; import { ViewsService } from '~/services/views.service'; import NcPluginMgrv2 from '~/helpers/NcPluginMgrv2'; import { BulkDataAliasService } from '~/services/bulk-data-alias.service'; +import NcConnectionMgrv2 from '~/utils/common/NcConnectionMgrv2'; @Injectable() export class ImportService { @@ -152,6 +153,19 @@ export class ImportService { (a) => a.column_name === col.column_name, ); idMap.set(colRef.id, col.id); + + // setval for auto increment column in pg + if (base.type === 'pg') { + if (modelData.pgSerialLastVal) { + if (col.ai) { + const sqlClient = await NcConnectionMgrv2.getSqlClient(base); + await sqlClient.knex.raw( + `SELECT setval(pg_get_serial_sequence('??', ?), ?);`, + [table.table_name, col.column_name, modelData.pgSerialLastVal], + ); + } + } + } } tableReferences.set(modelData.id, table); diff --git a/packages/nocodb/src/services/projects.service.ts b/packages/nocodb/src/services/projects.service.ts index e03cf6a4ba..4027c4ce76 100644 --- a/packages/nocodb/src/services/projects.service.ts +++ b/packages/nocodb/src/services/projects.service.ts @@ -35,11 +35,11 @@ export class ProjectsService { user: { id: string; roles: Record }; query?: any; }) { - const projects = - extractRolesObj(param.user?.roles)[OrgUserRoles.SUPER_ADMIN] && - !['shared', 'starred', 'recent'].some((k) => k in param.query) - ? await Project.list(param.query) - : await ProjectUser.getProjectsList(param.user.id, param.query); + const projects = extractRolesObj(param.user?.roles)[ + OrgUserRoles.SUPER_ADMIN + ] + ? await Project.list(param.query) + : await ProjectUser.getProjectsList(param.user.id, param.query); return projects; } diff --git a/tests/playwright/pages/Dashboard/common/Cell/index.ts b/tests/playwright/pages/Dashboard/common/Cell/index.ts index 5f5d8ace99..73366bcf08 100644 --- a/tests/playwright/pages/Dashboard/common/Cell/index.ts +++ b/tests/playwright/pages/Dashboard/common/Cell/index.ts @@ -300,19 +300,30 @@ export class CellPageObject extends BasePage { if (type === 'bt') { const chips = cell.locator('.chips > .chip'); - await expect(await chips.count()).toBe(count); + expect(await chips.count()).toBe(count); for (let i = 0; i < value.length; ++i) { await chips.nth(i).locator('.name').waitFor({ state: 'visible' }); await chips.nth(i).locator('.name').scrollIntoViewIfNeeded(); - await expect(await chips.nth(i).locator('.name')).toHaveText(value[i]); + await expect(chips.nth(i).locator('.name')).toHaveText(value[i]); } return; } // verify chip count & contents if (count) { - await expect(await cell.innerText()).toContain(`${count} ${count === 1 ? options.singular : options.plural}`); + const expectedText = `${count} ${count === 1 ? options.singular : options.plural}`; + let retryCount = 0; + while (retryCount < 5) { + const receivedText = await linkText.innerText(); + if (receivedText.includes(expectedText)) { + break; + } + retryCount++; + // add delay of 100ms + await this.rootPage.waitForTimeout(100 * retryCount); + } + expect(await cell.innerText()).toContain(expectedText); } if (verifyChildList) { diff --git a/tests/playwright/tests/db/views/viewGridShare.spec.ts b/tests/playwright/tests/db/views/viewGridShare.spec.ts index 7d0c3d10b2..cdae92f96f 100644 --- a/tests/playwright/tests/db/views/viewGridShare.spec.ts +++ b/tests/playwright/tests/db/views/viewGridShare.spec.ts @@ -98,6 +98,8 @@ test.describe('Shared view', () => { await dashboard.goto(); await page.reload(); + // kludge: wait for 3 seconds to avoid flaky test + await page.waitForTimeout(5000); await dashboard.treeView.openTable({ title: 'Film' }); await dashboard.grid.toolbar.clickGroupBy(); @@ -106,6 +108,8 @@ test.describe('Shared view', () => { await page.goto(sharedLink); await page.reload(); + // kludge: wait for 3 seconds to avoid flaky test + await page.waitForTimeout(5000); await sharedPage.grid.cell.verify({ index: 0, columnHeader: 'Title', value: 'ZORRO ARK' }); });