Browse Source

Merge pull request #2525 from nocodb/develop

pull/2527/head
github-actions[bot] 2 years ago committed by GitHub
parent
commit
d64aff139d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 89
      .github/workflows/ci-cd.yml
  2. 40
      .github/workflows/unit-test.yml
  3. 31
      README.md
  4. 1
      package.json
  5. 8
      packages/nc-gui-v2/.gitignore
  6. 42
      packages/nc-gui-v2/README.md
  7. 6
      packages/nc-gui-v2/app.vue
  8. BIN
      packages/nc-gui-v2/assets/icon.png
  9. 30
      packages/nc-gui-v2/components/dashboard/TabView.vue
  10. 24
      packages/nc-gui-v2/components/dashboard/TreeView.vue
  11. 56
      packages/nc-gui-v2/components/smartsheet/Grid.vue
  12. 36
      packages/nc-gui-v2/components/tabs/Smartsheet.vue
  13. 41
      packages/nc-gui-v2/composables/metas.ts
  14. 36
      packages/nc-gui-v2/composables/project.ts
  15. 22
      packages/nc-gui-v2/composables/tabs.ts
  16. 32
      packages/nc-gui-v2/composables/user.ts
  17. 14
      packages/nc-gui-v2/helpers/errorUtils.ts
  18. 21
      packages/nc-gui-v2/layouts/default.vue
  19. 35
      packages/nc-gui-v2/nuxt.config.ts
  20. 16104
      packages/nc-gui-v2/package-lock.json
  21. 24
      packages/nc-gui-v2/package.json
  22. 67
      packages/nc-gui-v2/pages/dashboard/[projectId].vue
  23. 42
      packages/nc-gui-v2/pages/form.vue
  24. 17
      packages/nc-gui-v2/pages/index.vue
  25. 88
      packages/nc-gui-v2/pages/projects/index.vue
  26. 18
      packages/nc-gui-v2/pages/sample.vue
  27. 65
      packages/nc-gui-v2/pages/signin.vue
  28. 62
      packages/nc-gui-v2/pages/signup.vue
  29. 41
      packages/nc-gui-v2/plugins/api.ts
  30. 27
      packages/nc-gui-v2/plugins/primevue.ts
  31. 21
      packages/nc-gui-v2/plugins/vuetify.ts
  32. 8
      packages/nc-gui-v2/tailwind.config.js
  33. 5
      packages/nc-gui-v2/tsconfig.json
  34. 12
      packages/nc-gui/nuxt.config.js
  35. 3
      packages/noco-docs/content/en/FAQs.md
  36. 2
      packages/noco-docs/content/en/developer-resources/accessing-apis.md
  37. 14
      packages/noco-docs/content/en/developer-resources/graphql-apis.md
  38. 1
      packages/noco-docs/content/en/developer-resources/rest-apis.md
  39. 2
      packages/noco-docs/content/en/developer-resources/sdk.md
  40. 2
      packages/noco-docs/content/en/developer-resources/webhooks.md
  41. 2
      packages/noco-docs/content/en/engineering/architecture.md
  42. 2
      packages/noco-docs/content/en/engineering/repository-structure.md
  43. 2
      packages/noco-docs/content/en/engineering/timely-build.md
  44. 2
      packages/noco-docs/content/en/engineering/translation.md
  45. 2
      packages/noco-docs/content/en/getting-started/demos.md
  46. 28
      packages/noco-docs/content/en/getting-started/installation.md
  47. 4
      packages/noco-docs/content/en/getting-started/upgrading.md
  48. 9
      packages/noco-docs/content/en/index.md
  49. 2
      packages/noco-docs/content/en/setup-and-usages/app-store.md
  50. 2
      packages/noco-docs/content/en/setup-and-usages/audit.md
  51. 2
      packages/noco-docs/content/en/setup-and-usages/code-snippets.md
  52. 2
      packages/noco-docs/content/en/setup-and-usages/column-operations.md
  53. 2
      packages/noco-docs/content/en/setup-and-usages/column-types.md
  54. 2
      packages/noco-docs/content/en/setup-and-usages/dashboard.md
  55. 2
      packages/noco-docs/content/en/setup-and-usages/formulas.md
  56. 2
      packages/noco-docs/content/en/setup-and-usages/import-airtable-to-sql-database-within-a-minute-for-free.md
  57. 2
      packages/noco-docs/content/en/setup-and-usages/languages.md
  58. 2
      packages/noco-docs/content/en/setup-and-usages/link-to-another-record.md
  59. 2
      packages/noco-docs/content/en/setup-and-usages/lookup.md
  60. 2
      packages/noco-docs/content/en/setup-and-usages/meta-management.md
  61. 2
      packages/noco-docs/content/en/setup-and-usages/primary-key.md
  62. 2
      packages/noco-docs/content/en/setup-and-usages/primary-value.md
  63. 2
      packages/noco-docs/content/en/setup-and-usages/rollup.md
  64. 2
      packages/noco-docs/content/en/setup-and-usages/share-base.md
  65. 2
      packages/noco-docs/content/en/setup-and-usages/share-view.md
  66. 2
      packages/noco-docs/content/en/setup-and-usages/sync-schema.md
  67. 2
      packages/noco-docs/content/en/setup-and-usages/table-operations.md
  68. 1
      packages/noco-docs/content/en/setup-and-usages/team-and-auth.md
  69. 2
      packages/noco-docs/content/en/setup-and-usages/usage-information.md
  70. 2
      packages/noco-docs/content/en/setup-and-usages/views.md
  71. 5050
      packages/nocodb-sdk/package-lock.json
  72. 8
      packages/nocodb-sdk/package.json
  73. 16845
      packages/nocodb/package-lock.json
  74. 7
      packages/nocodb/package.json
  75. 80
      packages/nocodb/src/__tests__/unit/lib/meta/api/dataApis/dataAliasApis.test.ts
  76. 350
      packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/BaseModelSqlv2.ts
  77. 15
      packages/nocodb/src/lib/meta/api/dataApis/dataAliasApis.ts
  78. 2
      packages/nocodb/src/lib/meta/api/tableApis.ts
  79. 2
      packages/nocodb/src/lib/migrations/v2/nc_013_sync_source.ts
  80. 9
      packages/nocodb/src/lib/migrations/v2/nc_016_alter_hooklog_payload_types.ts
  81. 49
      packages/nocodb/src/run/dockerRunPG_CyQuick.ts
  82. 1
      packages/nocodb/tests/export-import/config.json
  83. 29
      packages/nocodb/tests/export-import/exportSchema.js
  84. 2
      packages/nocodb/tsconfig.json
  85. 2
      scripts/cypress/cypress.json
  86. 15
      scripts/cypress/integration/common/7b_import_from_airtable.js
  87. 267
      scripts/cypress/integration/common/9a_QuickTest.js
  88. 13
      scripts/cypress/integration/test/quickTest.js

89
.github/workflows/ci-cd.yml

@ -547,6 +547,95 @@ jobs:
name: cypress-xcdb-restMisc-run-cache-snapshots
path: scripts/cypress/screenshots
retention-days: 2
cy-quick-sqlite:
runs-on: ubuntu-20.04
steps:
- name: Setup Node
uses: actions/setup-node@v1
with:
node-version: 16.15.0
- name: Checkout
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Cache node modules
uses: actions/cache@v2
env:
cache-name: cache-node-modules
with:
# npm cache files are stored in `~/.npm` on Linux/macOS
path: ~/.npm
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-
- name: Set env
run: echo "NODE_ENV=test" >> $GITHUB_ENV
- name: Cypress run
uses: cypress-io/github-action@v2
with:
start: |
cp ./scripts/cypress/fixtures/quickTest/noco_0_91_7.db ./packages/nocodb/noco.db
npm run start:api:cache
npm run start:web
docker-compose -f ./scripts/docker-compose-cypress.yml up -d
spec: "./scripts/cypress/integration/test/quickTest.js"
wait-on: "http://localhost:8080, http://localhost:3000/_nuxt/runtime.js"
wait-on-timeout: 1200
config-file: scripts/cypress/cypress.json
- name: Upload screenshots
if: always()
uses: actions/upload-artifact@v2
with:
name: cy-quick-sqlite-snapshots
path: scripts/cypress/screenshots
retention-days: 2
cy-quick-pg:
runs-on: ubuntu-20.04
steps:
- name: Setup Node
uses: actions/setup-node@v1
with:
node-version: 16.15.0
- name: Checkout
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Cache node modules
uses: actions/cache@v2
env:
cache-name: cache-node-modules
with:
# npm cache files are stored in `~/.npm` on Linux/macOS
path: ~/.npm
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-
- name: Set env
run: echo "NODE_ENV=test" >> $GITHUB_ENV
- name: Cypress run
uses: cypress-io/github-action@v2
with:
start: |
docker-compose -f ./scripts/cypress/docker-compose-pg-cy-quick.yml up -d
npm run start:api:cache:pg:cyquick
npm run start:web
spec: "./scripts/cypress/integration/test/quickTest.js"
wait-on: "http://localhost:8080, http://localhost:3000/_nuxt/runtime.js"
wait-on-timeout: 1200
config-file: scripts/cypress/cypress.json
- name: Upload screenshots
if: always()
uses: actions/upload-artifact@v2
with:
name: cy-quick-pg-snapshots
path: scripts/cypress/screenshots
retention-days: 2
# docker:
# runs-on: ubuntu-latest
# steps:

40
.github/workflows/unit-test.yml

@ -0,0 +1,40 @@
# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
name: Backend Unit Tests
on:
push:
branches: [ "develop" ]
pull_request:
branches: [ "develop" ]
jobs:
unit-tests:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16.x]
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
steps:
- uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- name: install dependencies nocodb-sdk
working-directory: ./packages/nocodb-sdk
run: npm ci
- name: build nocodb-sdk
working-directory: ./packages/nocodb-sdk
run: npm run build:main
- name: install dependencies nocodb
working-directory: ./packages/nocodb
run: npm ci
- name: run unit tests
working-directory: ./packages/nocodb
run: npm run unit-test

31
README.md

@ -148,6 +148,37 @@ nocodb/nocodb:latest
> If you plan to input some special characters, you may need to change the character set and collation yourself when creating the database. Please check out the examples for [MySQL Docker](https://github.com/nocodb/nocodb/issues/1340#issuecomment-1049481043).
## Binaries
##### MacOS (x64)
```bash
curl http://get.nocodb.com/macos-x64 -o nocodb -L && chmod +x nocodb && ./nocodb
```
##### MacOS (arm64)
```bash
curl http://get.nocodb.com/macos-arm64 -o nocodb -L && chmod +x nocodb && ./nocodb
```
##### Linux (x64)
```bash
curl http://get.nocodb.com/linux-x64 -o nocodb -L && chmod +x nocodb && ./nocodb
```
##### Linux (arm64)
```bash
curl http://get.nocodb.com/linux-arm64 -o nocodb -L && chmod +x nocodb && ./nocodb
```
##### Windows (x64)
```bash
iwp http://get.nocodb.com/win-x64
.\Noco-win-x64.exe
```
##### Windows (arm64)
```bash
iwp http://get.nocodb.com/win-arm64
.\Noco-win-arm64.exe
```
## Docker Compose

1
package.json

@ -19,6 +19,7 @@
"start:xcdb-api": "npm run build:common ; cd ./packages/nocodb; npm install ../nocodb-sdk;npm install; NC_DISABLE_CACHE=true NC_DISABLE_TELE=true NC_INFLECTION=camelize DATABASE_URL=sqlite:../../../scripts/cypress/fixtures/sqlite-sakila/sakila.db npm run watch:run:cypress",
"start:api:cache": "npm run build:common ; cd ./packages/nocodb; npm install ../nocodb-sdk;npm install; NC_DISABLE_TELE=true npm run watch:run:cypress",
"start:api:cache:pg": "npm run build:common ; cd ./packages/nocodb; npm install ../nocodb-sdk; npm install; NC_DISABLE_TELE=true npm run watch:run:cypress:pg",
"start:api:cache:pg:cyquick": "npm run build:common ; cd ./packages/nocodb; npm install ../nocodb-sdk; npm install; NC_DISABLE_TELE=true npm run watch:run:cypress:pg:cyquick",
"start:xcdb-api:cache": "npm run build:common ; cd ./packages/nocodb; npm install ../nocodb-sdk; npm install; NC_DISABLE_TELE=true NC_INFLECTION=camelize DATABASE_URL=sqlite:../../../scripts/cypress/fixtures/sqlite-sakila/sakila.db npm run watch:run:cypress",
"start:web": "npm run build:common ; cd ./packages/nc-gui; npm install ../nocodb-sdk; npm install; npm run dev",
"cypress:run": "cypress run --config-file ./scripts/cypress/cypress.json",

8
packages/nc-gui-v2/.gitignore vendored

@ -0,0 +1,8 @@
node_modules
*.log*
.nuxt
.nitro
.cache
.output
.env
dist

42
packages/nc-gui-v2/README.md

@ -0,0 +1,42 @@
# Nuxt 3 Minimal Starter
Look at the [nuxt 3 documentation](https://v3.nuxtjs.org) to learn more.
## Setup
Make sure to install the dependencies:
```bash
# yarn
yarn install
# npm
npm install
# pnpm
pnpm install --shamefully-hoist
```
## Development Server
Start the development server on http://localhost:3000
```bash
npm run dev
```
## Production
Build the application for production:
```bash
npm run build
```
Locally preview production build:
```bash
npm run preview
```
Checkout the [deployment documentation](https://v3.nuxtjs.org/guide/deploy/presets) for more information.

6
packages/nc-gui-v2/app.vue

@ -0,0 +1,6 @@
<template>
<!-- <NuxtLayout>-->
<!-- <NuxtPage />-->
<!-- </NuxtLayout>-->
<NuxtPage/>
</template>

BIN
packages/nc-gui-v2/assets/icon.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

30
packages/nc-gui-v2/components/dashboard/TabView.vue

@ -0,0 +1,30 @@
<template>
<div>
<TabMenu :model="tabItems" v-model:activeIndex="activeTab"/>
<template v-if="tabItems && tabItems[activeTab]">
<TabsSmartsheet :tab-meta="tabs[activeTab]" :key="tabs[activeTab].id"/>
</template>
</div>
</template>
<script setup lang="ts">
import {useTabs} from "~/composables/tabs";
const {tabs,activeTab} = useTabs()
const tabItems = computed(() => {
return tabs.value.map(tab => {
return {
label: tab.title,
// icon: tab.icon,
closable: true
}
})
})
</script>
<style scoped>
</style>

24
packages/nc-gui-v2/components/dashboard/TreeView.vue

@ -0,0 +1,24 @@
<template>
<div>
<div v-for="table in tables" class="p-2 text-sm pointer"
@click="addTab({type:'table',title:table.title, id:table.id})">
{{ table.title }}
</div>
</div>
</template>
<script setup lang="ts">
import {useProject} from "~/composables/project";
import {useTabs} from "~/composables/tabs";
const {tables} = useProject()
const {addTab} = useTabs()
</script>
<style scoped>
.pointer{
cursor: pointer;
}
</style>

56
packages/nc-gui-v2/components/smartsheet/Grid.vue

@ -0,0 +1,56 @@
<template>
<div>
<div class="card">
<DataTable :value="rows" responsiveLayout="scroll">
<Column v-for="col in meta.columns" :key="col.id" :field="col.title" :header="col.title">
<template v-if="col.uidt === 'LinkToAnotherRecord'" #body="{data:{[col.title]:d}}">
{{ d&& (Array.isArray(d) ? d : [d]).map(c1 => c1[Object.keys(c1)[1]]).join(', ') }}
</template>
</Column>
</DataTable>
</div>
</div>
</template>
<script lang="ts" setup>
import {useNuxtApp} from "#app";
import {Api} from "nocodb-sdk";
import {useUser} from "~/composables/user";
const {tabMeta, meta} = defineProps({
tabMeta: Object,
meta: Object
})
const {project} = useProject()
const {user} = useUser()
const rows = ref()
const {$api}: { $api: Api<any> } = useNuxtApp() as any
const loadData = async () => {
const response = await $api.dbTableRow.list('noco',
project.value.id,
meta.id, {}, {
headers: {
'xc-auth': user.token
}
})
rows.value = response.list
}
onMounted(async () => {
await loadData()
})
</script>
<style scoped>
</style>

36
packages/nc-gui-v2/components/tabs/Smartsheet.vue

@ -0,0 +1,36 @@
<template>
<div>
<template v-if="meta && tabMeta">
<SmartsheetGrid :meta="meta" :tabMeta="tabMeta"></SmartsheetGrid>
</template>
</div>
</template>
<script setup lang="ts">
import {useMetas} from "~/composables/metas";
import {computed, onMounted, watch} from 'vue'
const {tabMeta} = defineProps({
tabMeta: Object
})
const {getMeta, metas} = useMetas()
const meta = computed(() => {
return metas.value?.[tabMeta?.id]
})
onMounted(async () => {
await getMeta(tabMeta?.id)
})
watch(() => tabMeta && tabMeta?.id, async (newVal, oldVal) => {
if (newVal !== oldVal) {
await getMeta(newVal)
}
})
</script>
<style scoped>
</style>

41
packages/nc-gui-v2/composables/metas.ts

@ -0,0 +1,41 @@
import {useNuxtApp, useState} from "#app";
import {Api, TableType} from "nocodb-sdk";
import {useUser} from "~/composables/user";
import {useProject} from "~/composables/project";
export const useMetas = () => {
const {$api}: { $api: Api<any> } = useNuxtApp() as any
const {user} = useUser()
const {tables} = useProject()
const metas = useState<{ [idOrTitle: string]: TableType | any }>('metas', () => ({}))
const getMeta = async (tableIdOrTitle: string, force = false) => {
if (!force && metas[tableIdOrTitle]) {
return metas[tableIdOrTitle]
}
const modelId = (tables.value.find(t => t.title === tableIdOrTitle || t.id === tableIdOrTitle) || {}).id
if (!modelId) {
console.warn(`Table '${tableIdOrTitle}' is not found in the table list`)
return
}
const model = await $api.dbTable.read(modelId, {
headers: {
'xc-auth': user.token
}
})
metas.value = {
...metas.value,
[model.id]: model,
[model.title]: model
}
return model
}
return {getMeta, metas}
}

36
packages/nc-gui-v2/composables/project.ts

@ -0,0 +1,36 @@
import {useNuxtApp} from "#app";
import {Api, TableType} from "nocodb-sdk";
import {useUser} from "~/composables/user";
export const useProject = () => {
const {$api}: { $api: Api<any> } = useNuxtApp() as any
const {user} = useUser()
const project = useState<{ id?: string, title?: string }>('project', null)
const tables = useState<Array<TableType>>('tables', null)
const loadTables = async () => {
const tablesResponse = await $api.dbTable.list(project?.value?.id, {}, {
headers: {
'xc-auth': user.token
}
})
console.log(tablesResponse)
tables.value = tablesResponse.list
}
const loadProject = async (projectId:string) => {
const projectResponse = await $api.project.read(projectId, {
headers: {
'xc-auth': user.token
}
})
console.log(projectResponse)
project.value = projectResponse
}
return {project, tables, loadProject, loadTables}
}

22
packages/nc-gui-v2/composables/tabs.ts

@ -0,0 +1,22 @@
import {useState} from "#app";
interface TabItem {
type: 'table' | 'view',
title: string,
id:string
}
export const useTabs = () => {
const tabs = useState<Array<TabItem>>('tabs', () => [])
const activeTab = useState<number>('activeTab', ()=>0)
const addTab = (tabMeta: TabItem) => {
tabs.value = [...(tabs.value || []), tabMeta]
activeTab.value = tabs.value.length - 1
}
const clearTabs = () => {
tabs.value = []
}
return {tabs, addTab, activeTab, clearTabs}
}

32
packages/nc-gui-v2/composables/user.ts

@ -0,0 +1,32 @@
import {store} from 'nuxt3-store'
import {Api} from "nocodb-sdk";
import {useNuxtApp} from "#app";
export const useUser = () =>{
const user = store({
name: 'user',
type: 'localstorage',
value: {token: null, user : null},
reactiveType: 'reactive',
version: '1.0.0'
})
const {$api}: { $api: Api<any> } = useNuxtApp() as any
const getUser =async (args = {}) => {
const userInfo = await $api.auth.me(args, {
headers: {
'xc-auth': user.value.token
}
})
user.user = userInfo
}
const setToken = (token) => {
user.token = token
}
return {user,setToken, getUser}
}

14
packages/nc-gui-v2/helpers/errorUtils.ts

@ -0,0 +1,14 @@
export async function extractSdkResponseErrorMsg(e:Error & {response:any}) {
if (!e || !e.response) { return e.message }
let msg
if (e.response.data instanceof Blob) {
try {
msg = JSON.parse(await e.response.data.text()).msg
} catch {
msg = 'Some internal error occurred'
}
} else {
msg = e.response.data.msg || e.response.data.message || 'Some internal error occurred'
}
return msg || 'Some error occurred'
}

21
packages/nc-gui-v2/layouts/default.vue

@ -0,0 +1,21 @@
<template>
<div class="">
<!-- <div class="topbar">-->
<!-- </div>-->
<!-- <div class="sidebar">-->
<!-- </div>-->
<!-- <div class="content">-->
<slot></slot>
<!-- </div>-->
</div>
</template>
<script>
export default {
name: "default"
}
</script>
<style scoped>
</style>

35
packages/nc-gui-v2/nuxt.config.ts

@ -0,0 +1,35 @@
import {defineNuxtConfig} from 'nuxt'
// https://v3.nuxtjs.org/api/configuration/nuxt.config
export default defineNuxtConfig({
// modules: ['@nuxtjs/tailwindcss'],
// buildModules: [
// 'nuxt-vite'
// ],
modules: ['nuxt3-store'],
ssr:false,
plugins: [
// '~/plugins/vuetify.ts',
// '~/plugins/api.ts',
],
// css: ['vuetify/lib/styles/main.sass'],
// build: {
// transpile: ['vuetify']
// },
css: [
'primevue/resources/themes/saga-blue/theme.css',
'primevue/resources/primevue.css',
'primeicons/primeicons.css',
'primeflex/primeflex.css',
],
build: {
transpile: ['primevue']
},
vite: {
define: {
'process.env.DEBUG': 'false',
}
}
})

16104
packages/nc-gui-v2/package-lock.json generated

File diff suppressed because it is too large Load Diff

24
packages/nc-gui-v2/package.json

@ -0,0 +1,24 @@
{
"private": true,
"scripts": {
"build": "nuxt build",
"dev": "nuxt dev",
"generate": "nuxt generate",
"preview": "nuxt preview"
},
"devDependencies": {
"@nuxtjs/tailwindcss": "^5.1.2",
"fibers": "^5.0.1",
"nuxt": "3.0.0-rc.3",
"sass": "^1.53.0",
"sass-loader": "^10.3.0"
},
"dependencies": {
"nocodb-sdk": "file:../nocodb-sdk",
"nuxt3-store": "^1.0.0",
"vuetify": "^3.0.0-alpha.12",
"primevue": "3.10.0",
"primeflex": "3.2.0",
"primeicons": "5.0.0"
}
}

67
packages/nc-gui-v2/pages/dashboard/[projectId].vue

@ -0,0 +1,67 @@
<template>
<!-- todo: move to layout or create a reusable component -->
<div class="nc-container">
<div class="nc-topbar shadow-2">
</div>
<div class="nc-sidebar shadow-2 p-4 overflow-y-auto">
<DashboardTreeView></DashboardTreeView>
</div>
<div class="nc-content p-4 overflow-auto">
<DashboardTabView></DashboardTabView>
</div>
</div>
</template>
<script setup lang="ts">
import {useProject} from "~/composables/project";
import {watch} from "vue";
import {useTabs} from "~/composables/tabs";
const route = useRoute()
const {loadProject, loadTables} = useProject()
const {clearTabs} = useTabs()
onMounted(async () => {
await loadProject(route.params.projectId as string)
await loadTables()
})
watch(() => route.params.projectId, async (newVal, oldVal) => {
if (newVal && newVal !== oldVal) {
clearTabs()
await loadProject(newVal as string)
await loadTables()
}
})
</script>
<style scoped lang="scss">
.nc-container {
.nc-topbar {
position: fixed;
top: 0;
left: 0;
height: 50px;
width: 100%;
z-index: 5;
}
.nc-sidebar {
position: fixed;
top: 50px;
left: 0;
height: calc(100% - 50px);
width: 250px;
}
.nc-content {
position: fixed;
top: 50px;
left: 250px;
height: calc(100% - 50px);
width: calc(100% - 250px);
}
}
</style>

42
packages/nc-gui-v2/pages/form.vue

@ -0,0 +1,42 @@
<template>
<div class="container">
<Card style="width:500px">
<template #title>
Signup
</template>
<template #content>
<InputText type="text" v-model="value" label="Email"/>
</template>
<template #footer>
<Button label="Small" icon="pi pi-check" class="p-button-sm" />
</template>
</Card>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { useToast } from "primevue/usetoast";
const text = ref();
const toast = useToast();
const greet = () => {
toast.add({severity: 'info', summary: 'Hello ' + text.value});
}
</script>
<style lang="scss">
.container {
margin: 0 auto;
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
div {
display: flex;
flex-direction: column;
align-items: center;
margin-top: 1rem;
}
}
</style>

17
packages/nc-gui-v2/pages/index.vue

@ -0,0 +1,17 @@
<template>
<div class="container">
</div>
</template>
<script setup lang="ts">
import {useRouter} from "#app";
const $router = useRouter()
$router.replace('/projects')
</script>
<style lang="scss">
</style>

88
packages/nc-gui-v2/pages/projects/index.vue

@ -0,0 +1,88 @@
<template>
<!-- todo: move to layout or create a reusable component -->
<div class="nc-container">
<div class="nc-topbar shadow-2">
</div>
<div class="nc-sidebar shadow-2 p-4 overflow-y-auto">
</div>
<div class="nc-content p-4 overflow-auto">
<div class="grid">
<div class="col-3 p-3" v-for="project in projects" :key="project.id">
<Card @click="navigateToDashboard(project)">
<template #content>
<div class="text-center">
<h3>{{ project.title }}</h3>
</div>
</template>
</Card>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import {useRouter} from "#app";
const {$api} = useNuxtApp()
const {user} = useUser()
const $router = useRouter()
const projects = ref()
const loadProjects = async () => {
const projectsResponse = await $api.project.list({}, {
headers: {
'xc-auth': user.token
}
})
projects.value = projectsResponse.list
}
const navigateToDashboard = async (project) => {
await $router.push('/dashboard/' + project.id)
}
onMounted(async () => {
await loadProjects()
})
</script>
<style scoped lang="scss">
.nc-container {
.nc-topbar {
position: fixed;
top: 0;
left: 0;
height: 50px;
width: 100%;
z-index: 5;
}
.nc-sidebar {
position: fixed;
top: 50px;
left: 0;
height: calc(100% - 50px);
width: 250px;
}
.nc-content {
position: fixed;
top: 50px;
left: 250px;
height: calc(100% - 50px);
width: calc(100% - 250px);
}
}
</style>

18
packages/nc-gui-v2/pages/sample.vue

@ -0,0 +1,18 @@
<template>
<div>
<Sidebar :visible="true" position="left" :dismissable="false">
Content
</Sidebar>
</div>
</template>
<script>
export default {
name: "dashboard"
}
</script>
<style scoped>
</style>

65
packages/nc-gui-v2/pages/signin.vue

@ -0,0 +1,65 @@
<template>
<div>
<!-- Enter your work email -->
<Card class="p-4 mx-auto mt-5" style="max-width: 500px">
<template #content>
<Message class="" v-if="error" severity="error">{{ error }}</Message>
<div class="p-float-label mt-5">
<InputText id="email" type="text" v-model="form.email" style="width:100%"/>
<label for="email">Email</label>
</div>
<!-- Enter your password -->
<div class="p-float-label mt-5">
<InputText id="password" type="password" v-model="form.password" style="width:100%"/>
<label for="password">Password</label>
</div>
<div class="text-center">
<Button
class="mt-5"
@click="signIn"
>
<b>Sign In</b>
</Button>
</div>
</template>
</Card>
</div>
</template>
<script setup lang="ts">
import {ref, reactive} from 'vue'
import {useUser} from "~/composables/user";
import {extractSdkResponseErrorMsg} from "~/helpers/errorUtils";
import {useNuxtApp, useRouter} from "#app";
const { $api} = useNuxtApp()
const $router = useRouter()
const valid = ref()
const error = ref()
const form = reactive({
email: '',
password: ''
})
const {user, setToken} = useUser()
const signIn = async () => {
error.value = null
try {
const {token} = await $api.auth.signin(form)
await setToken(token)
await $router.push('/projects')
} catch (e) {
error.value = await extractSdkResponseErrorMsg(e)
}
}
</script>
<style scoped>
</style>

62
packages/nc-gui-v2/pages/signup.vue

@ -0,0 +1,62 @@
<template>
<div>
<!-- Enter your work email -->
<Card class="p-4 mx-auto mt-5" style="max-width: 500px">
<template #content>
<Message class="" v-if="error" severity="error">{{ error }}</Message>
<div class="p-float-label mt-5">
<InputText id="email" type="text" v-model="form.email" style="width:100%"/>
<label for="email">Email</label>
</div>
<!-- Enter your password -->
<div class="p-float-label mt-5">
<InputText id="password" type="password" v-model="form.password" style="width:100%"/>
<label for="password">Password</label>
</div>
<div class="text-center">
<Button
class="mt-5"
@click="signUp"
>
<b>Sign Up</b>
</Button>
</div>
</template>
</Card>
</div>
</template>
<script setup lang="ts">
import {ref, reactive} from 'vue'
import {useUser} from "~/composables/user";
import {extractSdkResponseErrorMsg} from "~/helpers/errorUtils";
const {$api, $router} = useNuxtApp()
const valid = ref()
const error = ref()
const form = reactive({
email: '',
password: ''
})
const {user, setToken} = useUser()
const signUp = async () => {
error.value = null
try {
const {token} = await $api.auth.signup(form)
await setToken(token)
$router.push('/projects')
} catch (e) {
error.value = await extractSdkResponseErrorMsg(e)
}
}
</script>
<style scoped>
</style>

41
packages/nc-gui-v2/plugins/api.ts

@ -0,0 +1,41 @@
import { Api } from 'nocodb-sdk';
import {defineNuxtPlugin} from "nuxt3/app";
export default defineNuxtPlugin((nuxtApp) => {
// Doing something with nuxtApp
const api = getApi(null, null)
nuxtApp.provide('api', api)
return {
provide: {
api123:api
}
}
})
export function getApi($store, $axios) {
const api = new Api({
baseURL: 'http://localhost:8080',
headers: {
'xc-auth': $store?.state?.users?.token
}
})
if($axios) {
// overwrite with nuxt axios instance
api.instance = $axios
}
return api
}
//
// export default function({ store: $store, $axios, ...rest }, inject) {
// const api = getApi($store, $axios)
//
// inject('api', api)
// }

27
packages/nc-gui-v2/plugins/primevue.ts

@ -0,0 +1,27 @@
import {defineNuxtPlugin} from "#app";
import PrimeVue from "primevue/config";
import Button from "primevue/button";
import InputText from "primevue/inputtext";
import Toast from "primevue/toast";
import Card from "primevue/card";
import Sidebar from "primevue/sidebar";
import Message from "primevue/message";
import TabMenu from "primevue/tabmenu";
import DataTable from "primevue/datatable";
import Column from "primevue/column";
import ToastService from 'primevue/toastservice';
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.vueApp.use(PrimeVue, {ripple: true});
nuxtApp.vueApp.use(ToastService);
nuxtApp.vueApp.component('Button', Button);
nuxtApp.vueApp.component('InputText', InputText);
nuxtApp.vueApp.component('Toast', Toast);
nuxtApp.vueApp.component('Card', Card);
nuxtApp.vueApp.component('Sidebar', Sidebar);
nuxtApp.vueApp.component('Message', Message);
nuxtApp.vueApp.component('TabMenu', TabMenu);
nuxtApp.vueApp.component('DataTable', DataTable);
nuxtApp.vueApp.component('Column', Column);
//other components that you need
});

21
packages/nc-gui-v2/plugins/vuetify.ts

@ -0,0 +1,21 @@
import { createVuetify } from 'vuetify'
import {
VApp,
VAppBar,
VBtn
} from 'vuetify/components'
import {defineNuxtPlugin} from "nuxt/app";
// Import everything
// import * as components from 'vuetify/components'
export default defineNuxtPlugin((nuxtApp) => {
// const vuetify = createVuetify({
// components/*: {
// VApp,
// VAppBar,
// VBtn*/
// // }
// })
// nuxtApp.vueApp.use(vuetify)
})

8
packages/nc-gui-v2/tailwind.config.js

@ -0,0 +1,8 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [],
theme: {
extend: {},
},
plugins: [],
}

5
packages/nc-gui-v2/tsconfig.json

@ -0,0 +1,5 @@
{
// https://v3.nuxtjs.org/concepts/typescript
"extends": "./.nuxt/tsconfig.json",
}

12
packages/nc-gui/nuxt.config.js

@ -3,12 +3,6 @@ import MonacoEditorWebpackPlugin from 'monaco-editor-webpack-plugin'
// import HtmlWebpackPlugin from 'html-webpack-plugin';
const fs = require('fs')
const packageJson = JSON.parse(fs.readFileSync('../nc-lib-gui/package.json', 'utf8'))
const version = packageJson.version
.replace(/\.(\d+)$/, (_, v) => {
// if (v === '99') throw new Error('Package version reached 99')
return `.${++v}`
})
export default {
/*
** Nuxt rendering mode
@ -207,12 +201,6 @@ export default {
// config.output.publicPath = "./_nuxt/";
// NOTE: future release version of xc-lib-gui
// const fs = require('fs');
// const packageJson = JSON.parse(fs.readFileSync('../xc-lib-gui/package.json', 'utf8'));
// const version = packageJson.version.replace(/\.(\d+)$/, (_, v) => {
// // if (v === '99') throw new Error('Package version reached 99')
// return `.${++v}`
// });
if (process.env.targetEnv === 'DEV') {
// nightly build
// e.g. 0.84.2-20220220-1250

3
packages/noco-docs/content/en/FAQs.md

@ -1,6 +1,6 @@
---
title: 'FAQs'
description: 'FAQs'
description: 'General FAQs'
position: 1000000
category: 'FAQ'
menuTitle: 'FAQs'
@ -65,7 +65,6 @@ Auth Token is a JWT Token generated based on the logged-in user. By default, the
API Token is a Nano ID with a length of 40. If you are passing API Token, make sure that the header is called `xc-token`.
## Do you plan to have Enterprise Edition ?
For features that make sense for enterprises like below - yes

2
packages/noco-docs/content/en/developer-resources/accessing-apis.md

@ -1,6 +1,6 @@
---
title: 'Accessing APIs'
description: 'Accessing APIs'
description: 'How to access NocoDB APIs with Auth or API token?'
position: 1000
category: 'Developer Resources'
menuTitle: 'Accessing APIs'

14
packages/noco-docs/content/en/developer-resources/graphql-apis.md

@ -1,14 +0,0 @@
---
title: 'GraphQL APIs'
position: 1020
category: 'Developer Resources'
menuTitle: 'GraphQL APIs'
---
<alert type="danger">
GraphQL APIs, unfortunately, has been deprecated form v0.90 onwards - which means
- Users won't be able to create a GraphQL project nor use the GraphQL queries.
- For projects created before v0.90, we will convert it to REST API projects.
- The rationale behind is that GraphQL is a really small use case of NocoDB users and smart spreadsheet are usually flat representation of data (may be one level nested) which means there will be additional wrangling of json and GQL schema when users are changing the schema dynamically.
</alert>

1
packages/noco-docs/content/en/developer-resources/rest-apis.md

@ -1,5 +1,6 @@
---
title: 'REST APIs'
description: 'NocoDB REST API Overview'
position: 1010
category: 'Developer Resources'
menuTitle: 'REST APIs'

2
packages/noco-docs/content/en/developer-resources/sdk.md

@ -1,6 +1,6 @@
---
title: 'NocoDB SDK'
description: 'SDK'
description: 'NocoDB SDK Overview'
position: 1400
category: 'Developer Resources'
menuTitle: 'NocoDB SDK'

2
packages/noco-docs/content/en/developer-resources/webhooks.md

@ -1,6 +1,6 @@
---
title: "Webhooks"
description: "Webhooks"
description: "Webhooks allows user to trigger on certain operations on following database operations"
position: 1500
category: "Developer Resources"
menuTitle: "Webhooks"

2
packages/noco-docs/content/en/engineering/architecture.md

@ -1,6 +1,6 @@
---
title: "NocoDB Architecture"
description: "NocoDB Architecture"
description: "NocoDB Architecture Overview"
position: 4000
category: "Engineering"
menuTitle: "NocoDB Architecture"

2
packages/noco-docs/content/en/engineering/repository-structure.md

@ -1,6 +1,6 @@
---
title: "Repository Structure"
description: "Repository Structure"
description: "NocoDB Repository Structure Overview"
position: 3000
category: "Engineering"
menuTitle: "Repository Structure"

2
packages/noco-docs/content/en/engineering/timely-build.md

@ -1,6 +1,6 @@
---
title: "Timely Build"
description: "Timely Build"
description: "NocoDB provides Timely Build for Docker and Binary Executables"
position: 5000
category: "Engineering"
menuTitle: "Timely Build"

2
packages/noco-docs/content/en/engineering/translation.md

@ -1,6 +1,6 @@
---
title: "i18n"
description: "Contribute to NocoDB's i18n translation"
description: "How to contribute to NocoDB's i18n translation?"
position: 2000
category: "Engineering"
menuTitle: "i18n"

2
packages/noco-docs/content/en/getting-started/demos.md

@ -1,6 +1,6 @@
---
title: 'Demos'
description: 'Demos'
description: 'NocoDB Demos'
position: 30
category: 'Getting started'
fullscreen: true

28
packages/noco-docs/content/en/getting-started/installation.md

@ -20,7 +20,6 @@ Simple installation - takes about three minutes!
## Quick try
### 1-Click Deploy to Heroku
Before doing so, make sure you have a Heroku account. By default, an add-on Heroku Postgres will be used as meta database. You can see the connection string defined in `DATABASE_URL` by navigating to Heroku App Settings and selecting Config Vars.
@ -86,7 +85,6 @@ curl http://get.nocodb.com/macos-arm64 -o nocodb -L \
&& ./nocodb
```
##### Linux (x64)
```bash
@ -369,31 +367,7 @@ aws ecs create-service \
</alert>
## Development Setup
If you want to modify the source code,
- Start the backend locally
```bash
cd packages/nocodb
npm install
npm run watch:run
```
- Start the frontend locally
```bash
cd packages/nc-gui
npm install
npm run dev
```
- Open ``localhost:3000/dashboard`` in browser
<alert>
nocodb/packages/nocodb includes nc-lib-gui which is the built version of nc-gui hosted in npm registry. <br>
You can visit localhost:8000/dashboard in browser after starting the backend locally if you just want to modify the backend only.
</alert>
Please refer to [Development Setup](https://github.com/nocodb/nocodb#development-setup).
## Sample Demos

4
packages/noco-docs/content/en/getting-started/upgrading.md

@ -1,6 +1,6 @@
---
title: 'Upgrading'
description: 'Upgrading NocoDB : Docker, npm, Heroku!'
description: 'Upgrading NocoDB : Docker, Node, Heroku and Homebrew!'
position: 20
category: 'Getting started'
menuTitle: 'Upgrading'
@ -100,7 +100,7 @@ This will trigger the Heroku deployment. Your app should now be updated to the l
## Homebrew
Run following commands to upgrade Homebrew Noocodb version.
Run following commands to upgrade Homebrew Nocodb version.
```bash
# Update the local homebrew formulas

9
packages/noco-docs/content/en/index.md

@ -6,11 +6,6 @@ category: 'Welcome'
menuTitle: 'Introduction'
---
<alert type="warning">
This documentation is only for version 0.90 onwards. If you are looking for the previous versions, please check out
<a href="https://docs-prev.nocodb.com/" target="_blank">here</a>.
</alert>
## Welcome!
NocoDB is a no-code database platform that allows teams to collaborate and build applications with ease of a familiar and intuitive spreadsheet interface. This allows even non-developers or business users to become software creators.
@ -97,8 +92,8 @@ We encourage all contributors to commit messages following [Commit Message Conve
We require a CLA (Contributor License Agreement). This is a one-time process. Please click this [link](https://cla-assistant.io/nocodb/nocodb) to agree to the CLA for nocodb/nocodb.
You can also share your thoughts and discuss with our community members via [discord](https://discord.gg/5RgZmkW) or [Github Discussion](https://github.com/nocodb/nocodb/discussions). We also share our [Immediate Roadmap](https://github.com/nocodb/nocodb/projects/1) and all opinions are welcome.
You can also share your thoughts and discuss with our community members via [Discord](https://discord.gg/5RgZmkW) or [Github Discussion](https://github.com/nocodb/nocodb/discussions). We also share our [Immediate Roadmap](https://github.com/nocodb/nocodb/projects/1) and all opinions are welcome.
## Support
If you have any issues or questions, you can reach out for help in our [discord](https://discord.gg/5RgZmkW).
If you have any issues or questions, you can reach out for help in our [Discord](https://discord.gg/5RgZmkW).

2
packages/noco-docs/content/en/setup-and-usages/app-store.md

@ -1,6 +1,6 @@
---
title: 'App Store'
description: 'App Store'
description: 'NocoDB provides different integrations in three main categories in App Store'
position: 1100
category: 'Product'
menuTitle: 'App Store'

2
packages/noco-docs/content/en/setup-and-usages/audit.md

@ -1,6 +1,6 @@
---
title: 'Audit'
description: 'Audit log'
description: 'NocoDB provides all the user operation logs under Audit log'
position: 590
category: 'Product'
menuTitle: 'Audit'

2
packages/noco-docs/content/en/setup-and-usages/code-snippets.md

@ -1,6 +1,6 @@
---
title: 'Code Snippets'
description: 'Code Snippets'
description: 'Code Snippets Examples in different ways'
position: 540
category: 'Product'
menuTitle: 'Code Snippets'

2
packages/noco-docs/content/en/setup-and-usages/column-operations.md

@ -1,6 +1,6 @@
---
title: "Column Operations"
description: "Column Operations"
description: "Column Operations: Fields, Sort & Filter"
position: 520
category: "Product"
menuTitle: "Column Operations"

2
packages/noco-docs/content/en/setup-and-usages/column-types.md

@ -1,6 +1,6 @@
---
title: 'Column Types'
description: 'Column Types'
description: 'NocoDB Column Types Overview'
position: 530
category: 'Product'
menuTitle: 'Column Types'

2
packages/noco-docs/content/en/setup-and-usages/dashboard.md

@ -1,6 +1,6 @@
---
title: 'Dashboard'
description: 'Dashboard'
description: 'Accessing the Dashboard!'
position: 500
category: 'Product'
menuTitle: 'Dashboard'

2
packages/noco-docs/content/en/setup-and-usages/formulas.md

@ -1,6 +1,6 @@
---
title: "Formulas"
description: "Formulas"
description: "NocoDB Formulas Syntaxes and Functions"
position: 570
category: "Product"
menuTitle: "Formulas"

2
packages/noco-docs/content/en/setup-and-usages/import-airtable-to-sql-database-within-a-minute-for-free.md

@ -1,6 +1,6 @@
---
title: 'Import Airtable to NocoDB'
description: 'Import Airtable to NocoDB'
description: 'A complete import of your Airtable to any MySQL, Postgres, SQLite and SQL server databases within minutes'
position: 1150
category: 'Product'
menuTitle: 'Import Airtable to NocoDB'

2
packages/noco-docs/content/en/setup-and-usages/languages.md

@ -1,6 +1,6 @@
---
title: 'Languages'
description: 'Languages'
description: 'Multiple languages on Dashboard!'
position: 900
category: 'Product'
menuTitle: 'Languages'

2
packages/noco-docs/content/en/setup-and-usages/link-to-another-record.md

@ -1,6 +1,6 @@
---
title: "Link To Another Record"
description: "Link To Another Record"
description: "Understanding Link To Another Record (LTAR) Columns!"
position: 540
category: "Product"
menuTitle: "Link To Another Record"

2
packages/noco-docs/content/en/setup-and-usages/lookup.md

@ -1,6 +1,6 @@
---
title: "Lookup"
description: "Lookup"
description: "Understanding Lookup Column!"
position: 550
category: "Product"
menuTitle: "Lookup"

2
packages/noco-docs/content/en/setup-and-usages/meta-management.md

@ -1,6 +1,6 @@
---
title: 'Metadata'
description: 'Metadata'
description: 'NocoDB Project Metadata'
position: 600
category: 'Product'
menuTitle: 'Metadata'

2
packages/noco-docs/content/en/setup-and-usages/primary-key.md

@ -1,6 +1,6 @@
---
title: "Primary Key"
description: "Primary Key"
description: "Understanding Primary Key in NocoDB!"
position: 575
category: "Product"
menuTitle: "Primary Key"

2
packages/noco-docs/content/en/setup-and-usages/primary-value.md

@ -1,6 +1,6 @@
---
title: "Primary value"
description: "Primary value"
description: "Understanding Primary Value in NocoDB!"
position: 580
category: "Product"
menuTitle: "Primary value"

2
packages/noco-docs/content/en/setup-and-usages/rollup.md

@ -1,6 +1,6 @@
---
title: "Rollup"
description: "Rollup"
description: "Understanding Rollup Column!"
position: 560
category: "Product"
menuTitle: "Rollup"

2
packages/noco-docs/content/en/setup-and-usages/share-base.md

@ -1,6 +1,6 @@
---
title: "Share Base"
description: "Procedure to share a base & generating embedded iframe"
description: "Procedures to share a base & generating embedded iframe"
position: 615
category: "Product"
menuTitle: "Share Base"

2
packages/noco-docs/content/en/setup-and-usages/share-view.md

@ -1,6 +1,6 @@
---
title: "Share View"
description: "Procedure to share a view"
description: "Procedures to share a view"
position: 620
category: "Product"
menuTitle: "Share View"

2
packages/noco-docs/content/en/setup-and-usages/sync-schema.md

@ -1,6 +1,6 @@
---
title: 'Sync Schema'
description: 'Schema changes made to database from outside nocodb GUI can be synced'
description: 'Schema changes made to database from outside NocoDB GUI can be synced'
position: 610
category: 'Product'
menuTitle: 'Sync Schema'

2
packages/noco-docs/content/en/setup-and-usages/table-operations.md

@ -1,6 +1,6 @@
---
title: "Table Operations"
description: "Table Operations"
description: "Table Operations: Row, Column, Quick Import, Export & Import"
position: 510
category: "Product"
menuTitle: "Table Operations"

1
packages/noco-docs/content/en/setup-and-usages/team-and-auth.md

@ -6,7 +6,6 @@ category: 'Product'
menuTitle: 'Team & Auth'
---
Team & Auth can be found by clicking `Team & Settings` from the left navigation drawer and clicking `Team & Auth`.
![image](https://user-images.githubusercontent.com/35857179/161902474-fd06678c-a171-4237-b171-dc028b3753de.png)

2
packages/noco-docs/content/en/setup-and-usages/usage-information.md

@ -6,8 +6,6 @@ category: 'Product'
menuTitle: 'Usage Information'
---
<announcement></announcement>
NocoDB is a fast growing open source project which is UI heavy and we are committed to providing a solution that exceeds the expectations of the users and community.
We are also committed to continuing to develop and make NocoDB even better than it is today.
To that end, NocoDB contains a feature in which anonymous and otherwise non-sensitive data is collected.

2
packages/noco-docs/content/en/setup-and-usages/views.md

@ -1,6 +1,6 @@
---
title: 'Views'
description: 'Views'
description: 'Understanding Views in NocoDB!'
position: 600
category: 'Product'
menuTitle: 'Views'

5050
packages/nocodb-sdk/package-lock.json generated

File diff suppressed because it is too large Load Diff

8
packages/nocodb-sdk/package.json

@ -60,6 +60,7 @@
"eslint-plugin-eslint-comments": "^3.2.0",
"eslint-plugin-functional": "^3.0.2",
"eslint-plugin-import": "^2.22.0",
"eslint-plugin-prettier": "^4.0.0",
"gh-pages": "^3.1.0",
"npm-run-all": "^4.1.5",
"nyc": "^15.1.0",
@ -67,9 +68,8 @@
"prettier": "^2.1.1",
"standard-version": "^9.0.0",
"ts-node": "^9.0.0",
"typedoc": "^0.19.0",
"typescript": "^4.0.2",
"eslint-plugin-prettier": "^4.0.0"
"typedoc": "^0.22.17",
"typescript": "^4.0.2"
},
"files": [
"build/main",
@ -106,4 +106,4 @@
"**/*.spec.js"
]
}
}
}

16845
packages/nocodb/package-lock.json generated

File diff suppressed because it is too large Load Diff

7
packages/nocodb/package.json

@ -17,6 +17,7 @@
"fix:prettier": "prettier \"src/**/*.ts\" --write",
"lint": "eslint src --ext .ts",
"test": "cross-env TS_NODE_PROJECT=tsconfig.json mocha -r ts-node/register src/__tests__/**/*.test.ts --recursive",
"unit-test": "cross-env TS_NODE_PROJECT=tsconfig.json mocha --require ts-node/register 'src/__tests__/unit/**/*.test.ts' --recursive --check-leaks --exit",
"local:test:graphql": "cross-env DATABASE_URL=mysql://root:password@localhost:3306/sakila TS_NODE_PROJECT=tsconfig.json mocha -r ts-node/register src/__tests__/graphql.test.ts --recursive --timeout 10000 --exit",
"test:graphql": "cross-env TS_NODE_PROJECT=tsconfig.json mocha -r ts-node/register src/__tests__/graphql.test.ts --recursive --timeout 10000 --exit",
"test:grpc": "cross-env TS_NODE_PROJECT=tsconfig.json mocha -r ts-node/register src/__tests__/grpc.test.ts --recursive --timeout 10000 --exit",
@ -70,6 +71,7 @@
"watch:run": "cross-env NC_DISABLE_TELE1=true EE=true nodemon -e ts,js -w ./src -x \"ts-node src/run/docker --log-error --project tsconfig.json\"",
"watch:run:cypress": "cross-env EE=true nodemon -e ts,js -w ./src -x \"ts-node src/run/docker --log-error --project tsconfig.json\"",
"watch:run:cypress:pg": "cross-env EE=true nodemon -e ts,js -w ./src -x \"ts-node src/run/dockerRunPG --log-error --project tsconfig.json\"",
"watch:run:cypress:pg:cyquick": "cross-env EE=true nodemon -e ts,js -w ./src -x \"ts-node src/run/dockerRunPG_CyQuick.ts --log-error --project tsconfig.json\"",
"watch:run:mysql": "cross-env NC_DISABLE_TELE=true EE=true nodemon -e ts,js -w ./src -x \"ts-node src/run/dockerRunMysql --log-error --project tsconfig.json\"",
"watch:run:pg": "cross-env NC_DISABLE_TELE=true EE=true nodemon -e ts,js -w ./src -x \"ts-node src/run/dockerRunPG --log-error --project tsconfig.json\"",
"run": "ts-node src/run/docker",
@ -197,6 +199,7 @@
"@types/minio": "^7.0.7",
"@types/mkdirp": "^1.0.2",
"@types/mocha": "^8.0.1",
"@types/node": "^18.0.0",
"@types/nodemailer": "^6.4.0",
"@types/supertest": "^2.0.10",
"@typescript-eslint/eslint-plugin": "^4.0.1",
@ -221,8 +224,10 @@
"npm-run-all": "^4.1.5",
"nyc": "^14.1.1",
"open-cli": "^5.0.0",
"prettier": "^1.19.1",
"prettier": "^2.7.1",
"raw-body": "^2.4.1",
"rewire": "^6.0.0",
"sinon": "^14.0.0",
"standard-version": "^8.0.1",
"supertest": "^4.0.2",
"terser-webpack-plugin": "^4.1.0",

80
packages/nocodb/src/__tests__/unit/lib/meta/api/dataApis/dataAliasApis.test.ts

@ -0,0 +1,80 @@
'use strict';
import rewire from 'rewire';
import sinon from 'sinon';
import { expect } from 'chai';
import Base from '../../../../../../lib/models/Base';
import Model from '../../../../../../lib/models/Model';
import NcConnectionMgrv2 from '../../../../../../lib/utils/common/NcConnectionMgrv2';
import * as getAstObject from '../../../../../../lib/db/sql-data-mapper/lib/sql/helpers/getAst';
const dataAliasApis = rewire(
'../../../../../../lib/meta/api/dataApis/dataAliasApis'
);
const getFindOne = dataAliasApis.__get__('getFindOne');
describe('getFindOne', () => {
const model = {
id: 'modelId',
base_id: 'baseId'
};
const view = {
id: 'viewId'
};
const base = {
id: 'baseId'
};
const dbDriver = {
id: 'dbDriverId'
};
const req = { query: {} };
const baseModel = {
findOne: sinon.fake.returns(undefined)
};
const baseGetFake = sinon.replace(Base, 'get', sinon.fake.returns(base));
const baseModelFake = sinon.replace(
Model,
'getBaseModelSQL',
sinon.fake.returns(baseModel)
);
sinon.replace(NcConnectionMgrv2, 'get', sinon.fake.returns(dbDriver));
const getAstFake = sinon.fake.returns({ id: 1 });
sinon.replace(getAstObject, 'default', getAstFake);
it('calls Base.get to find base', async () => {
await getFindOne(model, view, req);
expect(baseGetFake.calledWith(model.base_id)).to.be.true;
});
it('call Model.getBaseModelSQL to find baseModel', async () => {
await getFindOne(model, view, req);
expect(
baseModelFake.calledWith({
id: model.id,
viewId: view.id,
dbDriver: dbDriver
})
).to.be.true;
});
it('calls baseModel.findOne', async () => {
await getFindOne(model, view, req);
expect(baseModel.findOne.calledWith({ ...req.query })).to.be.true;
});
describe('when data is not found', () => {
it('should return empty object', async () => {
expect(await getFindOne(model, view, req)).to.be.empty;
});
});
describe('when data is found', () => {
it('returns data', async () => {
const findOneResult = {
id: 'dataId'
};
baseModel.findOne = sinon.fake.returns(findOneResult);
expect(await getFindOne(model, view, req)).eql(findOneResult);
});
});
});

350
packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/BaseModelSqlv2.ts

File diff suppressed because it is too large Load Diff

15
packages/nocodb/src/lib/meta/api/dataApis/dataAliasApis.ts

@ -135,12 +135,15 @@ async function getFindOne(model, view: View, req) {
args.sortArr = JSON.parse(args.sortArrJson);
} catch (e) {}
return await nocoExecute(
await getAst({ model, query: args, view }),
await baseModel.findOne(args),
{},
{}
);
const data = await baseModel.findOne(args);
return data
? await nocoExecute(
await getAst({ model, query: args, view }),
data,
{},
{}
)
: {};
}
async function getDataGroupBy(model, view: View, req) {

2
packages/nocodb/src/lib/meta/api/tableApis.ts

@ -169,7 +169,7 @@ export async function tableCreate(req: Request<any, any, TableReqType>, res) {
ip: (req as any).clientIp
}).then(() => {});
mapDefaultPrimaryValue(columns);
mapDefaultPrimaryValue(req.body.columns);
Tele.emit('evt', { evt_type: 'table:created' });

2
packages/nocodb/src/lib/migrations/v2/nc_013_sync_source.ts

@ -17,7 +17,7 @@ const up = async (knex: Knex) => {
table.string('project_id', 128);
table.foreign('project_id').references(`${MetaTable.PROJECT}.id`);
table.string('fk_user_id', 128);
table.string('fk_user_id', 20);
table.foreign('fk_user_id').references(`${MetaTable.USERS}.id`);
table.timestamps(true, true);

9
packages/nocodb/src/lib/migrations/v2/nc_016_alter_hooklog_payload_types.ts

@ -2,7 +2,14 @@ import Knex from 'knex';
import { MetaTable } from '../../utils/globals';
const up = async (knex: Knex) => {
if (knex.client.config.client !== 'sqlite3') {
if (knex.client.config.client === 'mssql') {
await knex.schema.alterTable(MetaTable.HOOK_LOGS, table => {
table.dropColumn('payload');
});
await knex.schema.alterTable(MetaTable.HOOK_LOGS, table => {
table.text('payload');
});
} else if (knex.client.config.client !== 'sqlite3') {
await knex.schema.alterTable(MetaTable.HOOK_LOGS, table => {
table.text('payload').alter();
});

49
packages/nocodb/src/run/dockerRunPG_CyQuick.ts

@ -0,0 +1,49 @@
import cors from 'cors';
import express from 'express';
import Noco from '../lib/Noco';
process.env.NC_VERSION = '0009044';
const server = express();
server.use(
cors({
exposedHeaders: 'xc-db-response',
})
);
server.set('view engine', 'ejs');
process.env[
`NC_DB`
] = `pg://localhost:5432?u=postgres&p=password&d=meta_v2_2022_06_13`;
process.env[`DEBUG`] = 'xc*';
(async () => {
const httpServer = server.listen(process.env.PORT || 8080, () => {
console.log(`App started successfully.\nVisit -> ${Noco.dashboardUrl}`);
});
server.use(await Noco.init({}, httpServer, server));
})().catch((e) => console.log(e));
/**
* @copyright Copyright (c) 2021, Xgene Cloud Ltd
*
* @author Naveen MR <oof1lab@gmail.com>
* @author Pranav C Balan <pranavxc@gmail.com>
*
* @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 <http://www.gnu.org/licenses/>.
*
*/

1
packages/nocodb/tests/export-import/config.json

@ -1,6 +1,7 @@
{
"srcProject": "sample",
"dstProject": "sample-copy",
"excludeDt": true,
"baseURL": "http://localhost:8080",
"xc-auth": "Copy Auth Token"
}

29
packages/nocodb/tests/export-import/exportSchema.js

@ -30,22 +30,23 @@ function removeEmpty(obj) {
);
}
function addColumnSpecificData(c) {
// pick required fields to proceed further
let col = removeEmpty(
(({ id, title, column_name, uidt, dt, pk, pv, rqd, dtxp, system }) => ({
id,
title,
column_name,
uidt,
dt,
pk,
pv,
rqd,
dtxp,
system
}))(c)
);
let col;
if(inputConfig.excludeDt) {
col = removeEmpty(
(({ id, title, column_name, uidt, pk, pv, rqd, dtxp, system, ai }) => ({
id, title, column_name, uidt, pk, pv, rqd, dtxp, system, ai
}))(c)
);
} else {
col = removeEmpty(
(({ id, title, column_name, uidt, dt, pk, pv, rqd, dtxp, system, ai }) => ({
id, title, column_name, uidt, dt, pk, pv, rqd, dtxp, system, ai
}))(c)
);
}
switch (c.uidt) {
case UITypes.Formula:

2
packages/nocodb/tsconfig.json

@ -48,7 +48,7 @@
"es2017"
],
"types": [
"node"
"mocha", "node"
],
"typeRoots": [
"node_modules/@types",

2
scripts/cypress/cypress.json

@ -21,7 +21,7 @@
"test/pg-restViews.js",
"test/pg-restRoles.js",
"test/pg-restMisc.js",
"common/9a_QuickTest.js"
"test/quickTest.js"
],
"defaultCommandTimeout": 13000,
"pageLoadTimeout": 600000,

15
scripts/cypress/integration/common/7b_import_from_airtable.js

@ -2,6 +2,9 @@
//
import { isTestSuiteActive } from "../../support/page_objects/projectConstants";
import { projectsPage } from "../../support/page_objects/navigation";
import { mainPage } from "../../support/page_objects/mainPage";
let apiKey = ""
let sharedBase = ""
@ -12,6 +15,11 @@ export const genTest = (apiType, dbType) => {
before(() => {
apiKey = Cypress.env("airtable").apiKey;
sharedBase = Cypress.env("airtable").sharedBase;
mainPage.toolBarTopLeft(mainPage.HOME).click({force: true})
projectsPage.createProject({ dbType: "none", apiType: "REST", name: "importSample" }, {})
// projectsPage.openProject("importSample")
// cy.openTableTab("Film", 3)
});
after(() => {});
@ -31,14 +39,13 @@ export const genTest = (apiType, dbType) => {
cy.getActiveModal().find(".nc-btn-airtable-import").should('exist').click()
// it will take a while for import to finish
cy.getActiveModal().find(".nc-btn-go-dashboard", {timeout: 120000}).should('exist').click()
cy.getActiveModal().find(".nc-btn-go-dashboard", {timeout: 180000}).should('exist').click()
});
it("Verify Schema", () => {});
it("Verify Data", () => {});
});
};
/**
* @copyright Copyright (c) 2021, Xgene Cloud Ltd
*
@ -59,4 +66,4 @@ export const genTest = (apiType, dbType) => {
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
*/

267
scripts/cypress/integration/common/9a_QuickTest.js

@ -17,11 +17,11 @@ let records = {
Number: "1",
Value: "$1.00",
Percent: "0.01",
Duration: "60",
};
// links/ computed fields
let records2 = {
Duration: "00:01",
Done: true,
Date: "2022-05-31",
Rating: "1",
@ -32,6 +32,12 @@ let records2 = {
Producer: ["P1", "P2"]
};
let tn = [ "Film", "Actor", "Producer", ]
let cn = [ "Name", "Notes", "Status", "Tags", "Done", "Date", "Phone",
"Email", "URL", "Number", "Percent", "Duration", "Rating",
"Actor", "Status (from Actor)", "RollUp", "Computation", "Producer" ]
function openWebhook(index) {
cy.get(".nc-btn-webhook").should("exist").click();
cy.get(".nc-hook").eq(index).click({ force: true });
@ -62,94 +68,125 @@ function verifyWebhook(config) {
cy.get(".nc-icon-hook-navigate-left").click({force:true})
}
export const genTest = (apiType, dbType) => {
export const genTest = (apiType, dbType, testMode) => {
if (!isTestSuiteActive(apiType, dbType)) return;
describe(`Webhook`, () => {
describe(`Quick Tests`, () => {
let cellIdx = 1;
let columnCount = cn.length
if(testMode === 'AT_IMPORT') {
cellIdx = 3;
columnCount -= 3;
}
before(() => {
cy.task("copyFile")
loginPage.signIn(roles.owner.credentials);
projectsPage.openProject("sample");
if( testMode === 'CY_QUICK') {
// cy.task("copyFile")
loginPage.signIn(roles.owner.credentials);
projectsPage.openProject("sample");
}
});
after(() => {});
it("Verify Schema", () => {
cy.openTableTab("Film", 3)
// verify if all tables exist
for(let i=0; i<tn.length; i++)
cy.get(".nc-project-tree").contains(tn[i]).should('exist')
// for Film table, verify columns
for(let i=0; i<columnCount; i++)
cy.get(".nc-grid-header-row").find(`[data-col="${cn[i]}"]`).should('exist')
});
it("Verify Data types", () => {
cy.openTableTab("Film", 3);
// normal cells
for (let [key, value] of Object.entries(records)) {
mainPage.getCell(key, 1).contains(value).should("exist");
mainPage.getCell(key, cellIdx).contains(value).should("exist");
}
// checkbox
mainPage
.getCell("Done", 1)
.getCell("Done", cellIdx)
.find(".mdi-check-circle-outline")
.should(records2.Done ? "exist" : "not.exist");
// date
// duration
mainPage.getCell("Duration", cellIdx).find('input').then(($e) => {
expect($e[0].value).to.equal(records2.Duration)
})
// rating
mainPage
.getCell("Rating", 1)
.getCell("Rating", cellIdx)
.find("button.mdi-star")
.should("have.length", records2.Rating);
// verifying only one instance as its different for PG & SQLite
// for PG: its Actor1, Actor1
// for SQLite: its Actor1, Actor2
// LinkToAnotherRecord
mainPage.getCell("Actor", 1).scrollIntoView();
mainPage.getCell("Actor", cellIdx).scrollIntoView();
cy.get(
':nth-child(1) > [data-col="Actor"] > .nc-virtual-cell > .v-lazy > .d-100 > .chips > :nth-child(1) > .v-chip__content > .name'
`:nth-child(${cellIdx}) > [data-col="Actor"] > .nc-virtual-cell > .v-lazy > .d-100 > .chips > :nth-child(1) > .v-chip__content > .name`
)
.contains(records2.Actor[0])
.should("exist");
cy.get(
':nth-child(1) > [data-col="Actor"] > .nc-virtual-cell > .v-lazy > .d-100 > .chips > :nth-child(2) > .v-chip__content > .name'
)
.contains(records2.Actor[1])
.should("exist");
// mainPage.getCell("Actor", 1).find(".nc-virtual-cell > .v-lazy > .d-100 > .chips").eq(0).contains("Actor1").should('exist')
// mainPage.getCell("Actor", 1).find(".nc-virtual-cell > .v-lazy > .d-100 > .chips").eq(1).contains("Actor2").should('exist')
// cy.get(
// `:nth-child(${cellIdx}) > [data-col="Actor"] > .nc-virtual-cell > .v-lazy > .d-100 > .chips > :nth-child(2) > .v-chip__content > .name`
// )
// .contains(records2.Actor[1])
// .should("exist");
// lookup
mainPage.getCell("Status (from Actor)", 1).scrollIntoView();
mainPage.getCell("Status (from Actor)", cellIdx).scrollIntoView();
cy.get(
':nth-child(1) > [data-col="Status (from Actor)"] > .nc-virtual-cell > .v-lazy > .d-flex > :nth-child(1) > .v-chip__content > div > .set-item'
`:nth-child(${cellIdx}) > [data-col="Status (from Actor)"] > .nc-virtual-cell > .v-lazy > .d-flex > :nth-child(1) > .v-chip__content > div > .set-item`
)
.contains(records2["Status (from Actor)"][0])
.should("exist");
cy.get(
':nth-child(1) > [data-col="Status (from Actor)"] > .nc-virtual-cell > .v-lazy > .d-flex > :nth-child(2) > .v-chip__content > div > .set-item'
)
.contains(records2["Status (from Actor)"][1])
.should("exist");
// cy.get(
// `:nth-child(${cellIdx}) > [data-col="Status (from Actor)"] > .nc-virtual-cell > .v-lazy > .d-flex > :nth-child(2) > .v-chip__content > div > .set-item`
// )
// .contains(records2["Status (from Actor)"][1])
// .should("exist");
// rollup
mainPage.getCell("RollUp", 1).scrollIntoView();
// cy.get(':nth-child(1) > [data-col="RollUp"] > .nc-virtual-cell > .v-lazy > span').contains(records2.RollUp).should('exist')
cy.get(`:nth-child(1) > [data-col="RollUp"] > .nc-virtual-cell`)
.contains(records2.RollUp)
.should("exist");
// formula
mainPage.getCell("Computation", 1).scrollIntoView();
cy.get(
`:nth-child(1) > [data-col="Computation"] > .nc-virtual-cell`
)
.contains(records2.Computation)
.should("exist");
// ltar hm relation
mainPage.getCell("Producer", 1).scrollIntoView();
cy.get(
':nth-child(1) > [data-col="Producer"] > .nc-virtual-cell > .v-lazy > .d-100 > .chips > :nth-child(1) > .v-chip__content > .name'
)
.contains(records2.Producer[0])
.should("exist");
cy.get(
':nth-child(1) > [data-col="Producer"] > .nc-virtual-cell > .v-lazy > .d-100 > .chips > :nth-child(2) > .v-chip__content > .name'
)
.contains(records2.Producer[1])
.should("exist");
if( testMode === 'CY_QUICK') {
mainPage.getCell("RollUp", cellIdx).scrollIntoView();
cy.get(`:nth-child(${cellIdx}) > [data-col="RollUp"] > .nc-virtual-cell`)
.contains(records2.RollUp)
.should("exist");
// formula
mainPage.getCell("Computation", cellIdx).scrollIntoView();
cy.get(
`:nth-child(${cellIdx}) > [data-col="Computation"] > .nc-virtual-cell`
)
.contains(records2.Computation)
.should("exist");
// ltar hm relation
mainPage.getCell("Producer", cellIdx).scrollIntoView();
cy.get(
`:nth-child(${cellIdx}) > [data-col="Producer"] > .nc-virtual-cell > .v-lazy > .d-100 > .chips > :nth-child(1) > .v-chip__content > .name`
)
.contains(records2.Producer[0])
.should("exist");
cy.get(
`:nth-child(${cellIdx}) > [data-col="Producer"] > .nc-virtual-cell > .v-lazy > .d-100 > .chips > :nth-child(2) > .v-chip__content > .name`
)
.contains(records2.Producer[1])
.should("exist");
}
cy.closeTableTab("Film");
});
@ -210,42 +247,43 @@ export const genTest = (apiType, dbType) => {
});
it("Verify Webhooks", () => {
cy.openTableTab("Actor", 25);
openWebhook(0)
verifyWebhook({
title: "Webhook-1",
event: "After Insert",
notification: "URL",
type: "POST",
url: "http://localhost:9090/hook",
condition: false
})
cy.get("body").type("{esc}");
openWebhook(1)
verifyWebhook({
title: "Webhook-2",
event: "After Update",
notification: "URL",
type: "POST",
url: "http://localhost:9090/hook",
condition: false
})
cy.get("body").type("{esc}");
openWebhook(2)
verifyWebhook({
title: "Webhook-3",
event: "After Delete",
notification: "URL",
type: "POST",
url: "http://localhost:9090/hook",
condition: false
})
cy.get("body").type("{esc}");
cy.closeTableTab("Actor");
if( testMode === 'CY_QUICK') {
cy.openTableTab("Actor", 25);
openWebhook(0)
verifyWebhook({
title: "Webhook-1",
event: "After Insert",
notification: "URL",
type: "POST",
url: "http://localhost:9090/hook",
condition: false
})
cy.get("body").type("{esc}");
openWebhook(1)
verifyWebhook({
title: "Webhook-2",
event: "After Update",
notification: "URL",
type: "POST",
url: "http://localhost:9090/hook",
condition: false
})
cy.get("body").type("{esc}");
openWebhook(2)
verifyWebhook({
title: "Webhook-3",
event: "After Delete",
notification: "URL",
type: "POST",
url: "http://localhost:9090/hook",
condition: false
})
cy.get("body").type("{esc}");
cy.closeTableTab("Actor");
}
});
it("Pagination", () => {
@ -274,7 +312,8 @@ export const genTest = (apiType, dbType) => {
cy.get(".nc-grid-header-cell").contains('Name').should("be.visible");
cy.get(".nc-grid-header-cell").contains('Notes').should("be.visible");
cy.get(".nc-grid-header-cell").contains('Attachments').should("not.be.visible");
// fix me!
if(testMode !== 'AT_IMPORT') cy.get(".nc-grid-header-cell").contains('Attachments').should("not.be.visible");
cy.get(".nc-grid-header-cell").contains('Status').should("be.visible");
cy.get(".nc-grid-header-cell").contains('Film').should("be.visible");
@ -315,25 +354,47 @@ export const genTest = (apiType, dbType) => {
});
it("Views, bt relation", () => {
cy.openTableTab("Producer", 3)
cy.get('.nc-grid-view-item').should('have.length', 4)
cy.get('.nc-form-view-item').should('have.length', 4)
cy.get('.nc-gallery-view-item').should('have.length', 3)
// LinkToAnotherRecord hm relation
mainPage.getCell("FilmRead", 1).scrollIntoView();
cy.get(
':nth-child(1) > [data-col="FilmRead"] > .nc-virtual-cell > .v-lazy > .d-100 > .chips > :nth-child(1) > .v-chip__content > .name'
)
.contains('Movie-1')
.should("exist");
cy.closeTableTab("Producer")
if( testMode === 'CY_QUICK') {
cy.openTableTab("Producer", 3)
cy.get('.nc-grid-view-item').should('have.length', 4)
cy.get('.nc-form-view-item').should('have.length', 4)
cy.get('.nc-gallery-view-item').should('have.length', 3)
// LinkToAnotherRecord hm relation
mainPage.getCell("FilmRead", 1).scrollIntoView();
cy.get(
':nth-child(1) > [data-col="FilmRead"] > .nc-virtual-cell > .v-lazy > .d-100 > .chips > :nth-child(1) > .v-chip__content > .name'
)
.contains('Movie-1')
.should("exist");
cy.closeTableTab("Producer")
}
})
it("Delete Project", () => {
if( testMode === 'AT_IMPORT') {
mainPage.toolBarTopLeft(mainPage.HOME).click({force:true})
cy.get(`.mdi-delete-outline`, {
timeout: 10000,
})
.should("exist")
.last()
.click();
cy.getActiveModal()
.find("button")
.contains("Submit")
.should("exist")
.click();
cy.toastWait("deleted successfully");
}
});
});
};
genTest("rest", "xcdb");
// genTest("rest", "xcdb");
/**
* @copyright Copyright (c) 2021, Xgene Cloud Ltd

13
scripts/cypress/integration/test/quickTest.js

@ -1,14 +1,17 @@
let t7b = require("../common/7b_import_from_airtable");
let t9a = require("../common/9a_QuickTest");
const {
setCurrentMode,
} = require("../../support/page_objects/projectConstants");
// use 0 as mode to execute individual files (debug mode, skip pre-configs)
// use 1 mode if noco.db doesnt contain user credentials (full run over GIT)
const nocoTestSuite = (apiType, dbType) => {
setCurrentMode(apiType, dbType);
t9a.genTest(apiType, dbType);
// CY Migration verification / Quick test
t9a.genTest(apiType, dbType, "CY_QUICK");
// AT Import verification
t7b.genTest(apiType, dbType)
t9a.genTest(apiType, dbType, "AT_IMPORT");
};
nocoTestSuite("rest", "xcdb");
@ -33,4 +36,4 @@ nocoTestSuite("rest", "xcdb");
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
*/
Loading…
Cancel
Save