diff --git a/.github/workflows/cd-deploy-main.yaml b/.github/workflows/cd-deploy-main.yaml
index 9b6217c5ab30..5959d0abd9c6 100644
--- a/.github/workflows/cd-deploy-main.yaml
+++ b/.github/workflows/cd-deploy-main.yaml
@@ -5,6 +5,7 @@ on:
- main
jobs:
deploy-main:
+ timeout-minutes: 3
runs-on: ubuntu-latest
steps:
- name: Repository Dispatch
diff --git a/.github/workflows/cd-deploy-tag.yaml b/.github/workflows/cd-deploy-tag.yaml
index 17bee3d1a34f..cb07fe44687e 100644
--- a/.github/workflows/cd-deploy-tag.yaml
+++ b/.github/workflows/cd-deploy-tag.yaml
@@ -5,6 +5,7 @@ on:
- 'v*'
jobs:
deploy-tag:
+ timeout-minutes: 3
runs-on: ubuntu-latest
steps:
- name: Repository Dispatch
diff --git a/.github/workflows/ci-chrome-extension.yaml b/.github/workflows/ci-chrome-extension.yaml
index a78be9415597..63c7094f7044 100644
--- a/.github/workflows/ci-chrome-extension.yaml
+++ b/.github/workflows/ci-chrome-extension.yaml
@@ -3,19 +3,16 @@ on:
push:
branches:
- main
- paths:
- - 'package.json'
- - 'packages/twenty-chrome-extension/**'
+
pull_request:
- paths:
- - 'package.json'
- - 'packages/twenty-chrome-extension/**'
+
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
chrome-extension-build:
+ timeout-minutes: 15
runs-on: ubuntu-latest
env:
VITE_SERVER_BASE_URL: http://localhost:3000
@@ -26,7 +23,25 @@ jobs:
with:
access_token: ${{ github.token }}
- uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ - name: Check for changed files
+ id: changed-files
+ uses: tj-actions/changed-files@v11
+ with:
+ files: |
+ package.json
+ packages/twenty-chrome-extension/**
+
- name: Install dependencies
+ if: steps.changed-files.outputs.any_changed == 'true'
uses: ./.github/workflows/actions/yarn-install
- name: Chrome Extension / Run build
+ if: steps.changed-files.outputs.any_changed == 'true'
run: npx nx build twenty-chrome-extension
+
+ - name: Mark as Valid if No Changes
+ if: steps.changed-files.outputs.changed != 'true'
+ run: |
+ echo "No relevant changes detected. Marking as valid."
diff --git a/.github/workflows/ci-e2e.yml.bak b/.github/workflows/ci-e2e.yml.bak
new file mode 100644
index 000000000000..7bba72f1e77b
--- /dev/null
+++ b/.github/workflows/ci-e2e.yml.bak
@@ -0,0 +1,52 @@
+name: CI E2E Tests
+on:
+ push:
+ branches:
+ - main
+ pull_request:
+ branches:
+ - '**'
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+jobs:
+ test:
+ timeout-minutes: 30
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+ - uses: actions/setup-node@v4
+ with:
+ node-version: lts/*
+
+ - name: Check for changed files
+ id: changed-files
+ uses: tj-actions/changed-files@v11
+ with:
+ files: |
+ packages/**
+ playwright.config.ts
+
+ - name: Skip if no relevant changes
+ if: steps.changed-files.outputs.any_changed == 'false'
+ run: echo "No relevant changes detected. Marking as valid."
+
+ - name: Install dependencies
+ if: steps.changed-files.outputs.any_changed == 'true'
+ uses: ./.github/workflows/actions/yarn-install
+ - name: Install Playwright Browsers
+ if: steps.changed-files.outputs.any_changed == 'true'
+ run: yarn playwright install --with-deps
+ - name: Run Playwright tests
+ if: steps.changed-files.outputs.any_changed == 'true'
+ run: yarn test:e2e companies
+ - uses: actions/upload-artifact@v4
+ if: always()
+ with:
+ name: playwright-report
+ path: playwright-report/
+ retention-days: 30
diff --git a/.github/workflows/ci-front.yaml b/.github/workflows/ci-front.yaml
index b61506709f93..910286ff1beb 100644
--- a/.github/workflows/ci-front.yaml
+++ b/.github/workflows/ci-front.yaml
@@ -3,21 +3,16 @@ on:
push:
branches:
- main
- paths:
- - 'package.json'
- - 'packages/twenty-front/**'
- - 'packages/twenty-ui/**'
+
pull_request:
- paths:
- - 'package.json'
- - 'packages/twenty-front/**'
- - 'packages/twenty-ui/**'
+
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
front-sb-build:
+ timeout-minutes: 30
runs-on: ubuntu-latest
env:
REACT_APP_SERVER_BASE_URL: http://localhost:3000
@@ -29,21 +24,43 @@ jobs:
access_token: ${{ github.token }}
- name: Fetch local actions
uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ - name: Check for changed files
+ id: changed-files
+ uses: tj-actions/changed-files@v11
+ with:
+ files: |
+ package.json
+ packages/twenty-front/**
+ packages/twenty-ui/**
+
+ - name: Skip if no relevant changes
+ if: steps.changed-files.outputs.any_changed == 'false'
+ run: echo "No relevant changes. Skipping CI."
+
- name: Install dependencies
+ if: steps.changed-files.outputs.any_changed == 'true'
uses: ./.github/workflows/actions/yarn-install
- name: Diagnostic disk space issue
+ if: steps.changed-files.outputs.any_changed == 'true'
run: df -h
- name: Front / Restore Storybook Task Cache
+ if: steps.changed-files.outputs.any_changed == 'true'
uses: ./.github/workflows/actions/task-cache
with:
tag: scope:frontend
tasks: storybook:build
- name: Front / Write .env
+ if: steps.changed-files.outputs.any_changed == 'true'
run: npx nx reset:env twenty-front
- name: Front / Build storybook
+ if: steps.changed-files.outputs.any_changed == 'true'
run: npx nx storybook:build twenty-front
front-sb-test:
- runs-on: ci-8-cores
+ timeout-minutes: 30
+ runs-on: shipfox-8vcpu-ubuntu-2204
needs: front-sb-build
strategy:
matrix:
@@ -54,36 +71,72 @@ jobs:
steps:
- name: Fetch local actions
uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+ - name: Check for changed files
+ id: changed-files
+ uses: tj-actions/changed-files@v11
+ with:
+ files: |
+ packages/twenty-front/**
+ - name: Skip if no relevant changes
+ if: steps.changed-files.outputs.any_changed == 'false'
+ run: echo "No relevant changes. Skipping CI."
+
- name: Install dependencies
+ if: steps.changed-files.outputs.any_changed == 'true'
uses: ./.github/workflows/actions/yarn-install
- name: Install Playwright
+ if: steps.changed-files.outputs.any_changed == 'true'
run: cd packages/twenty-front && npx playwright install
- name: Front / Restore Storybook Task Cache
+ if: steps.changed-files.outputs.any_changed == 'true'
uses: ./.github/workflows/actions/task-cache
with:
tag: scope:frontend
tasks: storybook:build
- name: Front / Write .env
+ if: steps.changed-files.outputs.any_changed == 'true'
run: npx nx reset:env twenty-front
- name: Run storybook tests
+ if: steps.changed-files.outputs.any_changed == 'true'
run: npx nx storybook:serve-and-test:static twenty-front --configuration=${{ matrix.storybook_scope }}
front-sb-test-performance:
- runs-on: ci-8-cores
+ timeout-minutes: 30
+ runs-on: shipfox-8vcpu-ubuntu-2204
env:
REACT_APP_SERVER_BASE_URL: http://localhost:3000
NX_REJECT_UNKNOWN_LOCAL_CACHE: 0
steps:
- name: Fetch local actions
uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+ - name: Check for changed files
+ id: changed-files
+ uses: tj-actions/changed-files@v11
+ with:
+ files: |
+ packages/twenty-front/**
+
+ - name: Skip if no relevant changes
+ if: steps.changed-files.outputs.any_changed == 'false'
+ run: echo "No relevant changes. Skipping CI."
+
- name: Install dependencies
+ if: steps.changed-files.outputs.any_changed == 'true'
uses: ./.github/workflows/actions/yarn-install
- name: Install Playwright
+ if: steps.changed-files.outputs.any_changed == 'true'
run: cd packages/twenty-front && npx playwright install
- name: Front / Write .env
+ if: steps.changed-files.outputs.any_changed == 'true'
run: npx nx reset:env twenty-front
- name: Run storybook tests
- run: npx nx storybook:serve-and-test:static:performance twenty-front
+ if: steps.changed-files.outputs.any_changed == 'true'
+ run: npx nx run twenty-front:storybook:serve-and-test:static:performance
front-chromatic-deployment:
+ timeout-minutes: 30
if: contains(github.event.pull_request.labels.*.name, 'run-chromatic') || github.event_name == 'push'
needs: front-sb-build
runs-on: ubuntu-latest
@@ -95,21 +148,38 @@ jobs:
- uses: actions/checkout@v4
with:
fetch-depth: 0
+
+ - name: Check for changed files
+ id: changed-files
+ uses: tj-actions/changed-files@v11
+ with:
+ files: |
+ packages/twenty-front/**
+
+ - name: Skip if no relevant changes
+ if: steps.changed-files.outputs.any_changed == 'false'
+ run: echo "No relevant changes. Skipping CI."
+
- name: Install dependencies
+ if: steps.changed-files.outputs.any_changed == 'true'
uses: ./.github/workflows/actions/yarn-install
- name: Front / Restore Storybook Task Cache
+ if: steps.changed-files.outputs.any_changed == 'true'
uses: ./.github/workflows/actions/task-cache
with:
tag: scope:frontend
tasks: storybook:build
- name: Front / Write .env
+ if: steps.changed-files.outputs.any_changed == 'true'
run: |
cd packages/twenty-front
touch .env
echo "REACT_APP_SERVER_BASE_URL: $REACT_APP_SERVER_BASE_URL" >> .env
- name: Publish to Chromatic
+ if: steps.changed-files.outputs.any_changed == 'true'
run: npx nx run twenty-front:chromatic:ci
front-task:
+ timeout-minutes: 30
runs-on: ubuntu-latest
env:
NX_REJECT_UNKNOWN_LOCAL_CACHE: 0
@@ -125,20 +195,35 @@ jobs:
uses: actions/checkout@v4
with:
fetch-depth: 0
+ - name: Check for changed files
+ id: changed-files
+ uses: tj-actions/changed-files@v11
+ with:
+ files: |
+ packages/twenty-front/**
+
+ - name: Skip if no relevant changes
+ if: steps.changed-files.outputs.any_changed == 'false'
+ run: echo "No relevant changes. Skipping CI."
+
- name: Install dependencies
+ if: steps.changed-files.outputs.any_changed == 'true'
uses: ./.github/workflows/actions/yarn-install
- name: Front / Restore ${{ matrix.task }} task cache
+ if: steps.changed-files.outputs.any_changed == 'true'
uses: ./.github/workflows/actions/task-cache
with:
tag: scope:frontend
tasks: ${{ matrix.task }}
- name: Reset .env
+ if: steps.changed-files.outputs.any_changed == 'true'
uses: ./.github/workflows/actions/nx-affected
with:
tag: scope:frontend
tasks: reset:env
- name: Run ${{ matrix.task }} task
+ if: steps.changed-files.outputs.any_changed == 'true'
uses: ./.github/workflows/actions/nx-affected
with:
tag: scope:frontend
- tasks: ${{ matrix.task }}
\ No newline at end of file
+ tasks: ${{ matrix.task }}
diff --git a/.github/workflows/ci-release-create.yaml b/.github/workflows/ci-release-create.yaml
index 068d054cb30b..736c57352597 100644
--- a/.github/workflows/ci-release-create.yaml
+++ b/.github/workflows/ci-release-create.yaml
@@ -15,6 +15,7 @@ on:
jobs:
create_pr:
+ timeout-minutes: 10
runs-on: ubuntu-latest
steps:
- name: Checkout
diff --git a/.github/workflows/ci-release-merge.yaml b/.github/workflows/ci-release-merge.yaml
index 96b719318672..5b0bce577501 100644
--- a/.github/workflows/ci-release-merge.yaml
+++ b/.github/workflows/ci-release-merge.yaml
@@ -6,6 +6,7 @@ on:
jobs:
tag_and_release:
+ timeout-minutes: 10
runs-on: ubuntu-latest
if: github.event.pull_request.merged == true && contains(github.event.pull_request.labels.*.name, 'release')
steps:
diff --git a/.github/workflows/ci-server.yaml b/.github/workflows/ci-server.yaml
index 074d63fdda40..5d7a35244f01 100644
--- a/.github/workflows/ci-server.yaml
+++ b/.github/workflows/ci-server.yaml
@@ -3,21 +3,16 @@ on:
push:
branches:
- main
- paths:
- - 'package.json'
- - 'packages/twenty-server/**'
- - 'packages/twenty-emails/**'
+
pull_request:
- paths:
- - 'package.json'
- - 'packages/twenty-server/**'
- - 'packages/twenty-emails/**'
+
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
server-setup:
+ timeout-minutes: 30
runs-on: ubuntu-latest
env:
NX_REJECT_UNKNOWN_LOCAL_CACHE: 0
@@ -38,25 +33,42 @@ jobs:
uses: actions/checkout@v4
with:
fetch-depth: 0
+
+ - name: Check for changed files
+ id: changed-files
+ uses: tj-actions/changed-files@v11
+ with:
+ files: |
+ package.json
+ packages/twenty-server/**
+ packages/twenty-emails/**
+
- name: Install dependencies
+ if: steps.changed-files.outputs.any_changed == 'true'
uses: ./.github/workflows/actions/yarn-install
- name: Server / Restore Task Cache
+ if: steps.changed-files.outputs.any_changed == 'true'
uses: ./.github/workflows/actions/task-cache
with:
tag: scope:backend
- name: Server / Run lint & typecheck
+ if: steps.changed-files.outputs.any_changed == 'true'
uses: ./.github/workflows/actions/nx-affected
with:
tag: scope:backend
tasks: lint,typecheck
- name: Server / Build
+ if: steps.changed-files.outputs.any_changed == 'true'
run: npx nx build twenty-server
- name: Server / Write .env
+ if: steps.changed-files.outputs.any_changed == 'true'
run: npx nx reset:env twenty-server
- name: Worker / Run
+ if: steps.changed-files.outputs.any_changed == 'true'
run: npx nx run twenty-server:worker:ci
server-test:
+ timeout-minutes: 30
runs-on: ubuntu-latest
needs: server-setup
env:
@@ -66,19 +78,33 @@ jobs:
uses: actions/checkout@v4
with:
fetch-depth: 0
+
+ - name: Check for changed files
+ id: changed-files
+ uses: tj-actions/changed-files@v11
+ with:
+ files: |
+ package.json
+ packages/twenty-server/**
+ packages/twenty-emails/**
+
- name: Install dependencies
+ if: steps.changed-files.outputs.any_changed == 'true'
uses: ./.github/workflows/actions/yarn-install
- name: Server / Restore Task Cache
+ if: steps.changed-files.outputs.any_changed == 'true'
uses: ./.github/workflows/actions/task-cache
with:
tag: scope:backend
- name: Server / Run Tests
+ if: steps.changed-files.outputs.any_changed == 'true'
uses: ./.github/workflows/actions/nx-affected
with:
tag: scope:backend
tasks: test
server-integration-test:
+ timeout-minutes: 30
runs-on: ubuntu-latest
needs: server-setup
services:
@@ -100,17 +126,30 @@ jobs:
uses: actions/checkout@v4
with:
fetch-depth: 0
+
+ - name: Check for changed files
+ id: changed-files
+ uses: tj-actions/changed-files@v11
+ with:
+ files: |
+ package.json
+ packages/twenty-server/**
+ packages/twenty-emails/**
+
- name: Install dependencies
+ if: steps.changed-files.outputs.any_changed == 'true'
uses: ./.github/workflows/actions/yarn-install
- name: Server / Restore Task Cache
+ if: steps.changed-files.outputs.any_changed == 'true'
uses: ./.github/workflows/actions/task-cache
with:
tag: scope:backend
- name: Server / Run Integration Tests
+ if: steps.changed-files.outputs.any_changed == 'true'
uses: ./.github/workflows/actions/nx-affected
with:
tag: scope:backend
- tasks: "test:integration"
+ tasks: "test:integration:with-db-reset"
- name: Server / Upload reset-logs file
if: always()
uses: actions/upload-artifact@v4
diff --git a/.github/workflows/ci-test-docker-compose.yaml b/.github/workflows/ci-test-docker-compose.yaml
index 1496425c8511..d7202857a7b4 100644
--- a/.github/workflows/ci-test-docker-compose.yaml
+++ b/.github/workflows/ci-test-docker-compose.yaml
@@ -1,20 +1,31 @@
name: 'Test Docker Compose'
on:
pull_request:
- paths:
- - 'packages/twenty-docker/**'
+
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
test:
+ timeout-minutes: 10
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
+ - name: Check for changed files
+ id: changed-files
+ uses: tj-actions/changed-files@v11
+ with:
+ files: |
+ packages/twenty-docker/**
+ docker-compose.yml
+ - name: Skip if no relevant changes
+ if: steps.changed-files.outputs.any_changed != 'true'
+ run: echo "No relevant changes detected. Marking as valid."
- name: Run compose
+ if: steps.changed-files.outputs.any_changed == 'true'
run: |
echo "Patching docker-compose.yml..."
# change image to localbuild using yq
@@ -31,10 +42,7 @@ jobs:
cp .env.example .env
echo "Generating secrets..."
echo "# === Randomly generated secrets ===" >>.env
- echo "ACCESS_TOKEN_SECRET=$(openssl rand -base64 32)" >>.env
- echo "LOGIN_TOKEN_SECRET=$(openssl rand -base64 32)" >>.env
- echo "REFRESH_TOKEN_SECRET=$(openssl rand -base64 32)" >>.env
- echo "FILE_TOKEN_SECRET=$(openssl rand -base64 32)" >>.env
+ echo "APP_SECRET=$(openssl rand -base64 32)" >>.env
echo "POSTGRES_ADMIN_PASSWORD=$(openssl rand -base64 32)" >>.env
echo "Starting server..."
diff --git a/.github/workflows/ci-tinybird.yaml b/.github/workflows/ci-tinybird.yaml
new file mode 100644
index 000000000000..44328556408c
--- /dev/null
+++ b/.github/workflows/ci-tinybird.yaml
@@ -0,0 +1,35 @@
+name: CI Tinybird
+on:
+ push:
+ branches:
+ - main
+
+ pull_request:
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+jobs:
+ ci:
+ timeout-minutes: 10
+ runs-on: ubuntu-latest
+ uses: tinybirdco/ci/.github/workflows/ci.yml@main
+ steps:
+ - name: Check for changed files
+ id: changed-files
+ uses: tj-actions/changed-files@v11
+ with:
+ files: |
+ package.json
+ packages/twenty-tinybird/**
+
+ - name: Skip if no relevant changes
+ if: steps.changed-files.outputs.any_changed == 'false'
+ run: echo "No relevant changes. Skipping CI."
+
+ - name: Check twenty-tinybird package
+ with:
+ data_project_dir: packages/twenty-tinybird
+ tb_admin_token: ${{ secrets.TB_ADMIN_TOKEN }}
+ tb_host: https://api.eu-central-1.aws.tinybird.co
diff --git a/.github/workflows/ci-utils.yaml b/.github/workflows/ci-utils.yaml
index fccfca98d8ab..7a7ef38042dc 100644
--- a/.github/workflows/ci-utils.yaml
+++ b/.github/workflows/ci-utils.yaml
@@ -19,6 +19,7 @@ concurrency:
jobs:
danger-js:
+ timeout-minutes: 3
runs-on: ubuntu-latest
if: github.event.action != 'closed'
steps:
@@ -31,6 +32,7 @@ jobs:
DANGER_GITHUB_API_TOKEN: ${{ github.token }}
congratulate:
+ timeout-minutes: 3
runs-on: ubuntu-latest
if: github.event.action == 'closed' && github.event.pull_request.merged == true
steps:
diff --git a/.github/workflows/ci-website.yaml b/.github/workflows/ci-website.yaml
index d79345f3bf53..770381855bd9 100644
--- a/.github/workflows/ci-website.yaml
+++ b/.github/workflows/ci-website.yaml
@@ -3,19 +3,17 @@ on:
push:
branches:
- main
- paths:
- - 'package.json'
- - 'packages/twenty-website/**'
+
pull_request:
- paths:
- - 'package.json'
- - 'packages/twenty-website/**'
+
+
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
website-build:
+ timeout-minutes: 3
runs-on: ubuntu-latest
services:
postgres:
@@ -27,13 +25,29 @@ jobs:
- 5432:5432
steps:
- uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+ - name: Check for changed files
+ id: changed-files
+ uses: tj-actions/changed-files@v11
+ with:
+ files: 'package.json, packages/twenty-website/**'
+
- name: Install dependencies
+ if: steps.changed-files.outputs.changed == 'true'
uses: ./.github/workflows/actions/yarn-install
+
- name: Website / Run migrations
+ if: steps.changed-files.outputs.changed == 'true'
run: npx nx database:migrate twenty-website
env:
DATABASE_PG_URL: postgres://twenty:twenty@localhost:5432/default
- name: Website / Build Website
+ if: steps.changed-files.outputs.changed == 'true'
run: npx nx build twenty-website
env:
- DATABASE_PG_URL: postgres://twenty:twenty@localhost:5432/default
\ No newline at end of file
+ DATABASE_PG_URL: postgres://twenty:twenty@localhost:5432/default
+
+ - name: Mark as VALID
+ if: steps.changed-files.outputs.changed != 'true' # If no changes, mark as valid
+ run: echo "No relevant changes detected. CI is valid."
\ No newline at end of file
diff --git a/.github/workflows/playwright.yml.bak b/.github/workflows/playwright.yml.bak
deleted file mode 100644
index cffb50287629..000000000000
--- a/.github/workflows/playwright.yml.bak
+++ /dev/null
@@ -1,27 +0,0 @@
-name: Playwright Tests
-on:
- push:
- branches: [ main, master ]
- pull_request:
- branches: [ main, master ]
-jobs:
- test:
- timeout-minutes: 60
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v4
- - uses: actions/setup-node@v4
- with:
- node-version: lts/*
- - name: Install dependencies
- run: npm install -g yarn && yarn
- - name: Install Playwright Browsers
- run: yarn playwright install --with-deps
- - name: Run Playwright tests
- run: yarn test:e2e companies
- - uses: actions/upload-artifact@v4
- if: always()
- with:
- name: playwright-report
- path: playwright-report/
- retention-days: 30
diff --git a/.gitignore b/.gitignore
index c5bb33e003d1..20f974922280 100644
--- a/.gitignore
+++ b/.gitignore
@@ -29,3 +29,4 @@ storybook-static
.nyc_output
test-results/
dump.rdb
+.tinyb
diff --git a/.vscode/settings.json b/.vscode/settings.json
index d63c92973cfc..fde6cdfb1ffb 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -45,5 +45,5 @@
"search.exclude": {
"**/.yarn": true,
},
- "eslint.debug": true
+ "eslint.debug": true,
}
diff --git a/LICENSE b/LICENSE
index 0ad25db4bd1d..50a6a10a2861 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,3 +1,8 @@
+
+This project is mostly licensed under the GNU General Public License (GPL) as described below. However, certain files within this project are licensed under a different commercial license. These files are clearly marked with the following comment at the top of the file: /* @license Enterprise */
+Files with this comment are not licensed under the aGPL v3, but instead are subject to the commercial license terms defined later in this file.
+
+
GNU AFFERO GENERAL PUBLIC LICENSE
Version 3, 19 November 2007
@@ -659,3 +664,47 @@ specific requirements.
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU AGPL, see
.
+
+
+
+------------------------------------
+
+
+
+ The Twenty.com Commercial License (the “Commercial License”)
+ Copyright (c) 2023-present Twenty.com, PBC
+
+With regard to Twenty's Software:
+
+This part of the software and associated documentation files (the "Software") may only be
+used in production, if you (and any entity that you represent) have agreed to,
+and are in compliance with, the Terms available
+at https://twenty.com/legal/terms, or other agreements governing
+the use of the Software, as mutually agreed by you and Twenty.com, PBC ("Twenty"),
+and otherwise have a valid Twenty Enterprise Edition subscription
+for the correct number of hosts and seats as defined in the Commercial Terms.
+Subject to the foregoing sentence,
+you are free to modify this Software and publish patches to the Software. You agree
+that Twenty and/or its licensors (as applicable) retain all right, title and interest in
+and to all such modifications and/or patches, and all such modifications and/or
+patches may only be used, copied, modified, displayed, distributed, or otherwise
+exploited with a valid Commercial Subscription for the correct number of hosts and seats.
+Notwithstanding the foregoing, you may copy and modify the Software for development
+and testing purposes, without requiring a subscription. You agree that Twenty.Com and/or
+its licensors (as applicable) retain all right, title and interest in and to all such
+modifications. You are not granted any other rights beyond what is expressly stated herein.
+Subject to the foregoing, it is forbidden to copy, merge, publish, distribute, sublicense,
+and/or sell the Software.
+
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+For all third party components incorporated into the Twenty Software, those
+components are licensed under the original license provided by the owner of the
+applicable component.
\ No newline at end of file
diff --git a/Makefile b/Makefile
index 764dbd1531d4..af42f19d9fb0 100644
--- a/Makefile
+++ b/Makefile
@@ -1,26 +1,12 @@
-docker-dev-build:
- make -C packages/twenty-docker dev-build
-
-docker-dev-up:
- make -C packages/twenty-docker dev-up
-
-docker-dev-start:
- make -C packages/twenty-docker dev-start
-
-docker-dev-stop:
- make -C packages/twenty-docker dev-stop
-
-docker-dev-sh:
- make -C packages/twenty-docker dev-sh
-
postgres-on-docker:
- make -C packages/twenty-postgres provision-on-docker
-
-postgres-on-macos-arm:
- make -C packages/twenty-postgres provision-on-macos-arm
-
-postgres-on-macos-intel:
- make -C packages/twenty-postgres provision-on-macos-intel
-
-postgres-on-linux:
- make -C packages/twenty-postgres provision-on-linux
+ docker run \
+ --name twenty_postgres \
+ -e POSTGRES_USER=postgres \
+ -e POSTGRES_PASSWORD=postgres \
+ -e POSTGRES_DB=default \
+ -v twenty_db_data:/var/lib/postgresql/data \
+ -p 5432:5432 \
+ twentycrm/twenty-postgres:latest
+
+redis-on-docker:
+ docker run -d --name twenty_redis -p 6379:6379 redis/redis-stack-server:latest
\ No newline at end of file
diff --git a/README.md b/README.md
index 86ed0fe51a6f..d75db65e1504 100644
--- a/README.md
+++ b/README.md
@@ -1,12 +1,3 @@
-
-
-
-
-
diff --git a/install.sh b/install.sh
index 39eb096b8e25..34741b89b908 100755
--- a/install.sh
+++ b/install.sh
@@ -45,7 +45,7 @@ trap on_exit EXIT
# Use environment variables VERSION and BRANCH, with defaults if not set
version=${VERSION:-$(curl -s https://api.github.com/repos/twentyhq/twenty/releases/latest | grep '"tag_name":' | cut -d '"' -f 4)}
-branch=${BRANCH:-main}
+branch=${BRANCH:-$version}
echo "🚀 Using version $version and branch $branch"
@@ -72,7 +72,7 @@ done
echo "📁 Creating directory '$dir_name'"
mkdir -p "$dir_name" && cd "$dir_name" || { echo "❌ Failed to create/access directory '$dir_name'"; exit 1; }
-# Copy the twenty/packages/twenty-docker/docker-compose.yml file in it
+# Copy twenty/packages/twenty-docker/docker-compose.yml in it
echo -e "\t• Copying docker-compose.yml"
curl -sLo docker-compose.yml https://raw.githubusercontent.com/twentyhq/twenty/$branch/packages/twenty-docker/docker-compose.yml
@@ -91,10 +91,7 @@ fi
# Generate random strings for secrets
echo "# === Randomly generated secrets ===" >>.env
-echo "ACCESS_TOKEN_SECRET=$(openssl rand -base64 32)" >>.env
-echo "LOGIN_TOKEN_SECRET=$(openssl rand -base64 32)" >>.env
-echo "REFRESH_TOKEN_SECRET=$(openssl rand -base64 32)" >>.env
-echo "FILE_TOKEN_SECRET=$(openssl rand -base64 32)" >>.env
+echo "APP_SECRET=$(openssl rand -base64 32)" >>.env
echo "" >>.env
echo "POSTGRES_ADMIN_PASSWORD=$(openssl rand -base64 32)" >>.env
diff --git a/nx.json b/nx.json
index 35b6e7501700..19c6f5462cce 100644
--- a/nx.json
+++ b/nx.json
@@ -108,12 +108,11 @@
"storybook:build": {
"executor": "nx:run-commands",
"cache": true,
- "dependsOn": ["^build"],
"inputs": ["^default", "excludeTests"],
"outputs": ["{projectRoot}/{options.output-dir}"],
"options": {
"cwd": "{projectRoot}",
- "command": "storybook build",
+ "command": "VITE_DISABLE_ESLINT_CHECKER=true storybook build",
"output-dir": "storybook-static",
"config-dir": ".storybook"
}
@@ -192,16 +191,7 @@
"executor": "nx:run-commands",
"options": {
"commands": [
- "npx concurrently --kill-others --success=first -n SB,TEST 'nx storybook:serve:static {projectName} --port={args.port}' 'npx wait-on tcp:{args.port} && nx storybook:test {projectName} --port={args.port}'"
- ],
- "port": 6006
- }
- },
- "storybook:serve-and-test:static:performance": {
- "executor": "nx:run-commands",
- "options": {
- "commands": [
- "npx concurrently --kill-others --success=first -n SB,TEST 'nx storybook:serve:dev {projectName} --configuration=performance --port={args.port}' 'npx wait-on tcp:{args.port} && nx storybook:test:no-coverage {projectName} --port={args.port} --configuration=performance'"
+ "npx concurrently --kill-others --success=first -n SB,TEST 'nx storybook:serve:static {projectName} --port={args.port} --configuration={args.performance}' 'npx wait-on tcp:{args.port} && nx storybook:test {projectName} --port={args.port} --configuration={args.scope}'"
],
"port": 6006
}
diff --git a/oss-gg/twenty-content-challenges/1-create-youtube-video-about-20.md b/oss-gg/twenty-content-challenges/1-create-youtube-video-about-20.md
deleted file mode 100644
index 455b5e35bae3..000000000000
--- a/oss-gg/twenty-content-challenges/1-create-youtube-video-about-20.md
+++ /dev/null
@@ -1,21 +0,0 @@
-**Side Quest**: Create a YouTube Video about Twenty showcasing a specific way to use Twenty effectively.
-**Points**: 750 Points
-**Proof**: Add your oss handle and YouTube video link to the list below.
-
-Please follow the following schema:
-
----
-
-» 05-April-2024 by YOUR oss.gg HANDLE » YouTube Link: https://link.to/content
-
----
-
-////////////////////////////
-
-Your turn 👇
-
-////////////////////////////
-
-» 02-October-2024 by [yourhandle](https://oss.gg/yourhandle) YouTube Link: [YouTube](https://twenty.com/)
-
----
\ No newline at end of file
diff --git a/oss-gg/twenty-content-challenges/2-write-blog-post-about-20.md b/oss-gg/twenty-content-challenges/2-write-blog-post-about-20.md
deleted file mode 100644
index a4c4e6bee944..000000000000
--- a/oss-gg/twenty-content-challenges/2-write-blog-post-about-20.md
+++ /dev/null
@@ -1,21 +0,0 @@
-**Side Quest**: Write a blog post about sharing your experience using Twenty in a detailed format on any platform.
-**Points**: 750 Points
-**Proof**: Add your oss handle and blog link to the list below.
-
-Please follow the following schema:
-
----
-
-» 05-April-2024 by YOUR oss.gg HANDLE » blog Link: https://link.to/content
-
----
-
-////////////////////////////
-
-Your turn 👇
-
-////////////////////////////
-
-» 02-October-2024 by [yourhandle](https://oss.gg/yourhandle) blog Link: [blog](https://twenty.com/)
-
----
\ No newline at end of file
diff --git a/oss-gg/twenty-content-challenges/3-write-selfthost-guide-blog-post-20.md b/oss-gg/twenty-content-challenges/3-write-selfthost-guide-blog-post-20.md
deleted file mode 100644
index c7352ec430fc..000000000000
--- a/oss-gg/twenty-content-challenges/3-write-selfthost-guide-blog-post-20.md
+++ /dev/null
@@ -1,21 +0,0 @@
-**Side Quest**: Write a blog post about self-hosting Twenty in a detailed format on any platform.
-**Points**: 750 Points
-**Proof**: Add your oss handle and blog link to the list below.
-
-Please follow the following schema:
-
----
-
-» 05-April-2024 by YOUR oss.gg HANDLE » blog Link: https://link.to/content
-
----
-
-////////////////////////////
-
-Your turn 👇
-
-////////////////////////////
-
-» 02-October-2024 by [yourhandle](https://oss.gg/yourhandle) blog Link: [blog](https://twenty.com/)
-
----
\ No newline at end of file
diff --git a/oss-gg/twenty-content-challenges/4-create-promotional-video-20-share.md b/oss-gg/twenty-content-challenges/4-create-promotional-video-20-share.md
deleted file mode 100644
index e52cb43a4247..000000000000
--- a/oss-gg/twenty-content-challenges/4-create-promotional-video-20-share.md
+++ /dev/null
@@ -1,21 +0,0 @@
-**Side Quest**: Create a promotional video for Twenty and share it on social media.
-**Points**: 750 Points
-**Proof**: Add your oss handle and video link to the list below.
-
-Please follow the following schema:
-
----
-
-» 05-April-2024 by YOUR oss.gg HANDLE » video Link: https://link.to/content
-
----
-
-////////////////////////////
-
-Your turn 👇
-
-////////////////////////////
-
-» 02-October-2024 by [yourhandle](https://oss.gg/yourhandle) video Link: [video](https://twenty.com/)
-
----
\ No newline at end of file
diff --git a/oss-gg/twenty-design-challenges/1-design-promotional-poster-20-share.md b/oss-gg/twenty-design-challenges/1-design-promotional-poster-20-share.md
deleted file mode 100644
index b61f187117b5..000000000000
--- a/oss-gg/twenty-design-challenges/1-design-promotional-poster-20-share.md
+++ /dev/null
@@ -1,25 +0,0 @@
-**Side Quest**: Design a promotional poster of Twenty and share it on social media.
-**Points**: 300 Points
-**Proof**: Add your oss handle and poster link to the list below.
-
-Please follow the following schema:
-
----
-
-» 05-April-2024 by YOUR oss.gg HANDLE » poster Link: https://link.to/content
-
----
-
-////////////////////////////
-
-Your turn 👇
-
-////////////////////////////
-
-» 02-October-2024 by [yourhandle](https://oss.gg/yourhandle) poster Link: [poster](https://twenty.com/)
-
-» 11-October-2024 by [thefool76](https://oss.gg/thefool76) poster Link: [poster](https://drive.google.com/file/d/1cIC1eitvY6zKVTXKq2LnVrS_2Ho9H8-P/view?usp=sharing)
-
-» 12-October-2024 by [Ionfinisher](https://oss.gg/Ionfinisher) poster Link: [poster](https://x.com/ion_finisher/status/1845168965963628802)
-
----
diff --git a/oss-gg/twenty-design-challenges/2-design-new-logo-twenty.md b/oss-gg/twenty-design-challenges/2-design-new-logo-twenty.md
deleted file mode 100644
index ceee0fa8e4da..000000000000
--- a/oss-gg/twenty-design-challenges/2-design-new-logo-twenty.md
+++ /dev/null
@@ -1,28 +0,0 @@
-**Side Quest**: Design/Create new Twenty logo, tweet your design, and mention @twentycrm.
-**Points**: 300 Points
-**Proof**: Create a logo upload it on any of the platform and add your oss handle and logo link to the list below.
-
-Please follow the following schema:
-
----
-
-» 05-April-2024 by YOUR oss.gg HANDLE » Logo Link: https://link.to/content » tweet Link: https://link.to/content
-
----
-
-////////////////////////////
-
-Your turn 👇
-
-////////////////////////////
-
-» 08-October-2024 by [adityadeshlahre](https://oss.gg/adityadeshlahre) Logo Link: [logo](https://drive.google.com/drive/folders/13k22xMnX2fhnWK94vas_hO1t-ImqXcHZ?usp=drive_link) » tweet Link: [tweet](https://x.com/adityadeshlahre/status/1843354963176718374)
-
-» 11-October-2024 by [thefool76](https://oss.gg/thefool76) Logo Link: [logo](https://drive.google.com/file/d/1DxSwNY_i90kGgWzPQj5SxScBz_6r02l4/view?usp=sharing) » tweet Link: [tweet](https://x.com/thefool1135/status/1844693487067034008)
-
-» 13-October-2024 by [Atharva_404](https://oss.gg/Atharva-3000) Logo Link: [logo](https://drive.google.com/drive/folders/1XB7ELR7kPA4x7Fx5RQr8wo5etdZAZgcs?usp=drive_link) » tweet Link: [tweet](https://x.com/0x_atharva/status/1845421218914095453)
-
-» 13-October-2024 by [Ionfinisher](https://oss.gg/Ionfinisher) Logo Link: [logo](https://drive.google.com/file/d/1l9vE8CIjW9KfdioI5WKzxrdmvO8LR4j7/view?usp=drive_link) » tweet Link: [tweet](https://x.com/ion_finisher/status/1845466470429442163)
-
-
----
diff --git a/oss-gg/twenty-design-challenges/3-create-custom-interfact-theme-20.md b/oss-gg/twenty-design-challenges/3-create-custom-interfact-theme-20.md
deleted file mode 100644
index e51945ea9988..000000000000
--- a/oss-gg/twenty-design-challenges/3-create-custom-interfact-theme-20.md
+++ /dev/null
@@ -1,21 +0,0 @@
-**Side Quest**: Duplicate the Figma file from the main repo and customize the variables to create a unique interface theme for Twenty.
-**Points**: 750 Points
-**Proof**: Add your oss handle and Figma link to the list below.
-
-Please follow the following schema:
-
----
-
-» 05-April-2024 by YOUR oss.gg HANDLE » Figma Link: https://link.to/content
-
----
-
-////////////////////////////
-
-Your turn 👇
-
-////////////////////////////
-
-» 02-October-2024 by [yourhandle](https://oss.gg/yourhandle) Figma Link: [Figma](https://twenty.com/)
-
----
\ No newline at end of file
diff --git a/oss-gg/twenty-dev-challenges/1-write-migration-script-other-crm-to-20.md b/oss-gg/twenty-dev-challenges/1-write-migration-script-other-crm-to-20.md
deleted file mode 100644
index 249d8e158cfa..000000000000
--- a/oss-gg/twenty-dev-challenges/1-write-migration-script-other-crm-to-20.md
+++ /dev/null
@@ -1,21 +0,0 @@
-**Side Quest**: Develop a script to facilitate the migration of data from another CRM to Twenty.
-**Points**: 750 Points
-**Proof**: Add your oss handle and record video and share link to the list below. In video show the working proof of your created script.
-
-Please follow the following schema:
-
----
-
-» 05-April-2024 by YOUR oss.gg HANDLE » video Link: https://link.to/content
-
----
-
-////////////////////////////
-
-Your turn 👇
-
-////////////////////////////
-
-» 02-October-2024 by [yourhandle](https://oss.gg/yourhandle) video Link: [video](https://twenty.com/)
-
----
\ No newline at end of file
diff --git a/oss-gg/twenty-dev-challenges/2-create-raycast-integration-for-20.md b/oss-gg/twenty-dev-challenges/2-create-raycast-integration-for-20.md
deleted file mode 100644
index e4793c40d66f..000000000000
--- a/oss-gg/twenty-dev-challenges/2-create-raycast-integration-for-20.md
+++ /dev/null
@@ -1,21 +0,0 @@
-**Side Quest**: Develop an integration for Raycast that enables users to create records on any object within Twenty directly from Raycast.
-**Points**: 1500 Points
-**Proof**: Add your oss handle and record video and share link to the list below. In video show the workflow of the your integration created and perform some task.
-
-Please follow the following schema:
-
----
-
-» 05-April-2024 by YOUR oss.gg HANDLE » video Link: https://link.to/content
-
----
-
-////////////////////////////
-
-Your turn 👇
-
-////////////////////////////
-
-» 02-October-2024 by [yourhandle](https://oss.gg/yourhandle) video Link: [video](https://twenty.com/)
-
----
\ No newline at end of file
diff --git a/oss-gg/twenty-no-code-challenges/1-create-n8n-template-integrate-20-API.md b/oss-gg/twenty-no-code-challenges/1-create-n8n-template-integrate-20-API.md
deleted file mode 100644
index 6786e5a94553..000000000000
--- a/oss-gg/twenty-no-code-challenges/1-create-n8n-template-integrate-20-API.md
+++ /dev/null
@@ -1,21 +0,0 @@
-**Side Quest**: Create an n8n workflow that empowers Twenty by connecting it to another tool.
-**Points**: 750 Points
-**Proof**: Add your oss handle and template link to the list below.
-
-Please follow the following schema:
-
----
-
-» 05-April-2024 by YOUR oss.gg HANDLE » template Link: https://link.to/content
-
----
-
-////////////////////////////
-
-Your turn 👇
-
-////////////////////////////
-
-» 02-October-2024 by [yourhandle](https://oss.gg/yourhandle) template Link: [template](https://twenty.com/)
-
----
\ No newline at end of file
diff --git a/oss-gg/twenty-no-code-challenges/2-write-selfthost-guide-blog-post-20.md b/oss-gg/twenty-no-code-challenges/2-write-selfthost-guide-blog-post-20.md
deleted file mode 100644
index 58fa6de4d8d6..000000000000
--- a/oss-gg/twenty-no-code-challenges/2-write-selfthost-guide-blog-post-20.md
+++ /dev/null
@@ -1,21 +0,0 @@
-**Side Quest**: Write a comprehensive guide on how to integrate Twenty with marketing automation tool (n8n, Zapier). Include a concrete use case and explain how to leverage AI to write API requests for non-developers and share it.
-**Points**: 1500 Points
-**Proof**: Add your oss handle and guide link to the list below.
-
-Please follow the following schema:
-
----
-
-» 05-April-2024 by YOUR oss.gg HANDLE » guide Link: https://link.to/content
-
----
-
-////////////////////////////
-
-Your turn 👇
-
-////////////////////////////
-
-» 02-October-2024 by [yourhandle](https://oss.gg/yourhandle) guide Link: [guide](https://twenty.com/)
-
----
\ No newline at end of file
diff --git a/oss-gg/twenty-side-quest/1-quote-tweet-20-oss-gg-launch.md b/oss-gg/twenty-side-quest/1-quote-tweet-20-oss-gg-launch.md
deleted file mode 100644
index 168e311b9a32..000000000000
--- a/oss-gg/twenty-side-quest/1-quote-tweet-20-oss-gg-launch.md
+++ /dev/null
@@ -1,45 +0,0 @@
-**Side Quest**: Like & Re-Tweet oss.gg Launch Tweet. Quote-tweet it tagging @twentycrm to say you’ll be contributing.
-**Points**: 50 Points
-**Proof**: Add a screenshot of the retweet to the PR description. Add a link to your retweet in the list below.
-
-Please follow the following schema:
-
----
-
-» 05-April-2024 by YOUR NAME
-» Link to Tweet: https://x.com/...
-
----
-
-////////////////////////////
-
-Your turn 👇
-
-////////////////////////////
-
-» 13-October-2024 by Vanshika Dargan
-» Link to Tweet: https://x.com/VanshikaDargan/status/1845467453108949123
-
-» 13-October-2024 by Utsav Bhattarai
-» Link to Tweet: https://x.com/utsavdotdev/status/1845417863462649900
-
-» 10-October-2024 by Devansh Baghel
-» Link to Tweet: https://x.com/DevanshBaghel5/status/1844359648037748954
-
-» 11-October-2024 by Bhavesh Mishra
-» Link to Tweet: https://x.com/thefool1135/status/1844453425188405326
-
-» 11-October-2024 by Chirag Arora
-» Link to Tweet: https://x.com/Chirag8023/status/1844689900668682699
-
-» 11-October-2024 by Aritra Sadhukhan
-» Link to Tweet: https://x.com/AritraDevelops/status/1844670236512878646
-
-» 13-October-2024 by Nabhag Motivaras
-» Link to Tweet: https://x.com/NabhagMotivaras/status/1845449144695218357
-
-» 13-October-2024 by Ali Yar Khan
-» Link to Tweet: https://x.com/Mr_Programmer14/status/1845527862549577860
-
-» 13-October-2024 by Yash Parmar
-» Link to Tweet: https://x.com/yashp3020/status/1845720834716959009
diff --git a/oss-gg/twenty-side-quest/2-tweet-about-fav-twenty-feature.md b/oss-gg/twenty-side-quest/2-tweet-about-fav-twenty-feature.md
deleted file mode 100644
index 508210ae5a6e..000000000000
--- a/oss-gg/twenty-side-quest/2-tweet-about-fav-twenty-feature.md
+++ /dev/null
@@ -1,28 +0,0 @@
-**Side Quest**: Share a tweet about your favorite feature in Twenty. Tweet about your favorite feature in Twenty and mention @twentycrm.
-**Points**: 50 Points
-**Proof**: Add a screenshot of the tweet to the PR description. Add a link to your tweet in the list below.
-
-Please follow the following schema:
-
----
-
-» 05-April-2024 by YOUR NAME
-» Link to Tweet: https://x.com/...
-
----
-
-////////////////////////////
-
-Your turn 👇
-
-////////////////////////////
-
-» 10-October-2024 by Devansh Baghel
-» Link to Tweet: https://x.com/DevanshBaghel5/status/1844384722119704972
-
-» 11-October-2024 by Bhavesh Mishra
-» Link to Tweet: https://x.com/thefool1135/status/1844456500380696969
-
-» 13-October-2024 by Ali Yar Khan
-» Link to Tweet: https://x.com/Mr_Programmer14/status/1845530448245711197
----
diff --git a/oss-gg/twenty-side-quest/3-bug-report.md b/oss-gg/twenty-side-quest/3-bug-report.md
deleted file mode 100644
index d393a2cbeac8..000000000000
--- a/oss-gg/twenty-side-quest/3-bug-report.md
+++ /dev/null
@@ -1,23 +0,0 @@
-**Side Quest**: Create a bug report. Use the Twenty bug issue template to report a bug in detail, including steps to reproduce it.
-**Points**: 50-150 Points
-**Proof**: Add a link to your bug report in the list below.
-
-Please follow the following schema:
-
----
-
-» 05-April-2024 by YOUR NAME
-» Link to bug report: https://github.com/twentyhq/twenty/issues/...
-
----
-
-////////////////////////////
-
-Your turn 👇
-
-////////////////////////////
-
-» 10-October-2024 by Devansh Baghel
-» Link to bug report: https://github.com/twentyhq/twenty/issues/7560
-
----
diff --git a/oss-gg/twenty-side-quest/4-meme-magic.md b/oss-gg/twenty-side-quest/4-meme-magic.md
deleted file mode 100644
index b5cb3263d218..000000000000
--- a/oss-gg/twenty-side-quest/4-meme-magic.md
+++ /dev/null
@@ -1,34 +0,0 @@
-**Side Quest**: Meme Magic: Craft a meme where the number twenty plays a role. Tweet it, and tag @twentycrm.
-**Points**: 150 Points
-**Proof**: Add a screenshot of meme to the PR description. Add a link to your tweet in the list below.
-
-Please follow the following schema:
-
----
-
-» 05-April-2024 by YOUR NAME
-» Link to Tweet: https://x.com/...
-
----
-
-////////////////////////////
-
-Your turn 👇
-
-////////////////////////////
-
-» 10-October-2024 by Teddy ASSIH
-» Link to Tweet: https://x.com/ion_finisher/status/1844389252253299173
-
-» 11-October-2024 by Bhavesh Mishra
-» Link to Tweet: https://x.com/thefool1135/status/1844458836402503931
-
-» 12-October-2024 by Chirag Arora
-» Link to Tweet: https://x.com/Chirag8023/status/1845108226527994222
-
-» 13-October-2024 by Ali Yar Khan
-» Link to Tweet: https://x.com/Mr_Programmer14/status/1845537662587072697
-
-» 14-October-2024 by Yash Parmar
-» Link to Tweet: [https://x.com/yashp3020/status/1845108226527994222](https://x.com/yashp3020/status/1845720142702842093)
----
diff --git a/oss-gg/twenty-side-quest/5-gif-magic.md b/oss-gg/twenty-side-quest/5-gif-magic.md
deleted file mode 100644
index 20467fef4784..000000000000
--- a/oss-gg/twenty-side-quest/5-gif-magic.md
+++ /dev/null
@@ -1,33 +0,0 @@
-**Side Quest**: Gif Magic: Create a gif related to Twenty. Tweet it, and tag @twentycrm.
-**Points**: 150 Points
-**Proof**: Add a screenshot of GIF on Giphy to the PR description. Add a link to your GIPHY in the list below.
-
-Please follow the following schema:
-
----
-
-» 05-April-2024 by YOUR NAME
-» Link to gif: https://giphy.com/...
-
----
-
-////////////////////////////
-
-Your turn 👇
-
-////////////////////////////
-
-» 10-October-2024 by Teddy ASSIH
-» Link to gif: https://giphy.com/gifs/oss-crm-twenty-VWDHAIlGTbc6Nqdza9
-
-» 11-October-2024 by Bhavesh Mishra
-» Link to gif: https://shorturl.at/yln9H
-
-» 12-October-2024 by Chirag Arora
-» Link to gif: https://giphy.com/gifs/yCJIS2MGbBdifbnuj0
-
-» 13-October-2024 by Nabhag Motivaras
-» Link to gif: https://giphy.com/gifs/twenty-twentycrm-opensourcecrm-wCcsmnJuzzzGrfuf9B
-
-
----
diff --git a/oss-gg/twenty-side-quest/6-quest-wizard.md b/oss-gg/twenty-side-quest/6-quest-wizard.md
deleted file mode 100644
index 9543e3767d6f..000000000000
--- a/oss-gg/twenty-side-quest/6-quest-wizard.md
+++ /dev/null
@@ -1,19 +0,0 @@
-**Side Quest**: Complete all Twenty side quests
-**Points**: 300 Points
-**Proof**: Add screenshots for each side quest to the PR description. Add your name to the list below.
-
-Please follow the following schema:
-
----
-
- » 05-April-2024 by YOUR NAME
-
-////////////////////////////
-
-Your turn 👇
-
-////////////////////////////
-
-» 01-October-2024 by X
-
----
diff --git a/package.json b/package.json
index a4dc90df92ac..c9c7f6f1ef34 100644
--- a/package.json
+++ b/package.json
@@ -23,6 +23,7 @@
"@linaria/core": "^6.2.0",
"@linaria/react": "^6.2.1",
"@mdx-js/react": "^3.0.0",
+ "@microsoft/microsoft-graph-client": "^3.0.7",
"@nestjs/apollo": "^11.0.5",
"@nestjs/axios": "^3.0.1",
"@nestjs/cli": "^9.0.0",
@@ -49,6 +50,7 @@
"@stoplight/elements": "^8.0.5",
"@swc/jest": "^0.2.29",
"@tabler/icons-react": "^2.44.0",
+ "@tiptap/extension-hard-break": "^2.9.1",
"@types/dompurify": "^3.0.5",
"@types/facepaint": "^1.2.5",
"@types/lodash.camelcase": "^4.3.7",
@@ -176,6 +178,7 @@
"scroll-into-view": "^1.16.2",
"semver": "^7.5.4",
"sharp": "^0.32.1",
+ "slash": "^5.1.0",
"stripe": "^14.17.0",
"ts-key-enum": "^2.0.12",
"tslib": "^2.3.0",
@@ -199,6 +202,7 @@
"@graphql-codegen/typescript": "^3.0.4",
"@graphql-codegen/typescript-operations": "^3.0.4",
"@graphql-codegen/typescript-react-apollo": "^3.3.7",
+ "@microsoft/microsoft-graph-types": "^2.40.0",
"@nestjs/cli": "^9.0.0",
"@nestjs/schematics": "^9.0.0",
"@nestjs/testing": "^9.0.0",
@@ -294,6 +298,7 @@
"eslint-plugin-jsx-a11y": "^6.8.0",
"eslint-plugin-prefer-arrow": "^1.2.3",
"eslint-plugin-prettier": "^5.1.2",
+ "eslint-plugin-project-structure": "^3.9.1",
"eslint-plugin-react": "^7.33.2",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.4",
@@ -347,7 +352,7 @@
"version": "0.2.1",
"nx": {},
"scripts": {
- "start": "npx nx run-many -t start -p twenty-server twenty-front"
+ "start": "npx concurrently --kill-others 'npx nx run-many -t start -p twenty-server twenty-front' 'npx wait-on tcp:3000 && npx nx run twenty-server:worker'"
},
"workspaces": {
"packages": [
diff --git a/packages/twenty-chrome-extension/src/options/Loading.tsx b/packages/twenty-chrome-extension/src/options/Loading.tsx
index 1fde24f2b9e5..a0543c9c6dad 100644
--- a/packages/twenty-chrome-extension/src/options/Loading.tsx
+++ b/packages/twenty-chrome-extension/src/options/Loading.tsx
@@ -1,6 +1,5 @@
-import styled from '@emotion/styled';
-
import { Loader } from '@/ui/display/loader/components/Loader';
+import styled from '@emotion/styled';
const StyledContainer = styled.div`
align-items: center;
diff --git a/packages/twenty-docker/.env.example b/packages/twenty-docker/.env.example
index 59d8d03f93a7..b10e09876e52 100644
--- a/packages/twenty-docker/.env.example
+++ b/packages/twenty-docker/.env.example
@@ -3,16 +3,12 @@ TAG=latest
# POSTGRES_ADMIN_PASSWORD=replace_me_with_a_strong_password
PG_DATABASE_HOST=db:5432
+REDIS_URL=redis://redis:6379
SERVER_URL=http://localhost:3000
-# REDIS_HOST=redis
-# REDIS_PORT=6379
# Use openssl rand -base64 32 for each secret
-# ACCESS_TOKEN_SECRET=replace_me_with_a_random_string_access
-# LOGIN_TOKEN_SECRET=replace_me_with_a_random_string_login
-# REFRESH_TOKEN_SECRET=replace_me_with_a_random_string_refresh
-# FILE_TOKEN_SECRET=replace_me_with_a_random_string_refresh
+# APP_SECRET=replace_me_with_a_random_string
SIGN_IN_PREFILLED=true
diff --git a/packages/twenty-docker/docker-compose.yml b/packages/twenty-docker/docker-compose.yml
index b2efc1a168e4..41d80dabc398 100644
--- a/packages/twenty-docker/docker-compose.yml
+++ b/packages/twenty-docker/docker-compose.yml
@@ -25,8 +25,7 @@ services:
PG_DATABASE_URL: postgres://twenty:twenty@${PG_DATABASE_HOST}/default
SERVER_URL: ${SERVER_URL}
FRONT_BASE_URL: ${FRONT_BASE_URL:-$SERVER_URL}
- REDIS_PORT: ${REDIS_PORT:-6379}
- REDIS_HOST: ${REDIS_HOST:-redis}
+ REDIS_URL: ${REDIS_URL:-redis://localhost:6379}
ENABLE_DB_MIGRATIONS: "true"
@@ -36,10 +35,7 @@ services:
STORAGE_S3_NAME: ${STORAGE_S3_NAME}
STORAGE_S3_ENDPOINT: ${STORAGE_S3_ENDPOINT}
- ACCESS_TOKEN_SECRET: ${ACCESS_TOKEN_SECRET}
- LOGIN_TOKEN_SECRET: ${LOGIN_TOKEN_SECRET}
- REFRESH_TOKEN_SECRET: ${REFRESH_TOKEN_SECRET}
- FILE_TOKEN_SECRET: ${FILE_TOKEN_SECRET}
+ APP_SECRET: ${APP_SECRET}
depends_on:
change-vol-ownership:
condition: service_completed_successfully
@@ -59,8 +55,7 @@ services:
PG_DATABASE_URL: postgres://twenty:twenty@${PG_DATABASE_HOST}/default
SERVER_URL: ${SERVER_URL}
FRONT_BASE_URL: ${FRONT_BASE_URL:-$SERVER_URL}
- REDIS_PORT: ${REDIS_PORT:-6379}
- REDIS_HOST: ${REDIS_HOST:-redis}
+ REDIS_URL: ${REDIS_URL:-redis://localhost:6379}
ENABLE_DB_MIGRATIONS: "false" # it already runs on the server
@@ -69,10 +64,7 @@ services:
STORAGE_S3_NAME: ${STORAGE_S3_NAME}
STORAGE_S3_ENDPOINT: ${STORAGE_S3_ENDPOINT}
- ACCESS_TOKEN_SECRET: ${ACCESS_TOKEN_SECRET}
- LOGIN_TOKEN_SECRET: ${LOGIN_TOKEN_SECRET}
- REFRESH_TOKEN_SECRET: ${REFRESH_TOKEN_SECRET}
- FILE_TOKEN_SECRET: ${FILE_TOKEN_SECRET}
+ APP_SECRET: ${APP_SECRET}
depends_on:
db:
condition: service_healthy
diff --git a/packages/twenty-docker/k8s/manifests/deployment-server.yaml b/packages/twenty-docker/k8s/manifests/deployment-server.yaml
index b1229d649bbb..857729788675 100644
--- a/packages/twenty-docker/k8s/manifests/deployment-server.yaml
+++ b/packages/twenty-docker/k8s/manifests/deployment-server.yaml
@@ -41,10 +41,8 @@ spec:
value: "https://crm.example.com:443"
- name: "PG_DATABASE_URL"
value: "postgres://twenty:twenty@twenty-db.twentycrm.svc.cluster.local/default"
- - name: "REDIS_HOST"
- value: "twentycrm-redis.twentycrm.svc.cluster.local"
- - name: "REDIS_PORT"
- value: 6379
+ - name: "REDIS_URL"
+ value: "redis://twentycrm-redis.twentycrm.svc.cluster.local:6379"
- name: ENABLE_DB_MIGRATIONS
value: "true"
- name: SIGN_IN_PREFILLED
@@ -57,26 +55,11 @@ spec:
value: "7d"
- name: "LOGIN_TOKEN_EXPIRES_IN"
value: "1h"
- - name: ACCESS_TOKEN_SECRET
+ - name: APP_SECRET
valueFrom:
secretKeyRef:
name: tokens
key: accessToken
- - name: LOGIN_TOKEN_SECRET
- valueFrom:
- secretKeyRef:
- name: tokens
- key: loginToken
- - name: REFRESH_TOKEN_SECRET
- valueFrom:
- secretKeyRef:
- name: tokens
- key: refreshToken
- - name: FILE_TOKEN_SECRET
- valueFrom:
- secretKeyRef:
- name: tokens
- key: fileToken
ports:
- containerPort: 3000
name: http-tcp
diff --git a/packages/twenty-docker/k8s/manifests/deployment-worker.yaml b/packages/twenty-docker/k8s/manifests/deployment-worker.yaml
index b3a7e07a19aa..eb1938ba6dda 100644
--- a/packages/twenty-docker/k8s/manifests/deployment-worker.yaml
+++ b/packages/twenty-docker/k8s/manifests/deployment-worker.yaml
@@ -40,30 +40,13 @@ spec:
value: "bull-mq"
- name: "CACHE_STORAGE_TYPE"
value: "redis"
- - name: "REDIS_HOST"
- value: "twentycrm-redis.twentycrm.svc.cluster.local"
- - name: "REDIS_PORT"
- value: 6379
- - name: ACCESS_TOKEN_SECRET
+ - name: "REDIS_URL"
+ value: "redis://twentycrm-redis.twentycrm.svc.cluster.local:6379"
+ - name: APP_SECRET
valueFrom:
secretKeyRef:
name: tokens
key: accessToken
- - name: LOGIN_TOKEN_SECRET
- valueFrom:
- secretKeyRef:
- name: tokens
- key: loginToken
- - name: REFRESH_TOKEN_SECRET
- valueFrom:
- secretKeyRef:
- name: tokens
- key: refreshToken
- - name: FILE_TOKEN_SECRET
- valueFrom:
- secretKeyRef:
- name: tokens
- key: fileToken
command:
- yarn
- worker:prod
diff --git a/packages/twenty-docker/k8s/terraform/deployment-server.tf b/packages/twenty-docker/k8s/terraform/deployment-server.tf
index 1868b17624da..5276d574319e 100644
--- a/packages/twenty-docker/k8s/terraform/deployment-server.tf
+++ b/packages/twenty-docker/k8s/terraform/deployment-server.tf
@@ -61,12 +61,8 @@ resource "kubernetes_deployment" "twentycrm_server" {
value = "postgres://twenty:${var.twentycrm_pgdb_admin_password}@${kubernetes_service.twentycrm_db.metadata.0.name}.${kubernetes_namespace.twentycrm.metadata.0.name}.svc.cluster.local/default"
}
env {
- name = "REDIS_HOST"
- value = "${kubernetes_service.twentycrm_redis.metadata.0.name}.${kubernetes_namespace.twentycrm.metadata.0.name}.svc.cluster.local"
- }
- env {
- name = "REDIS_PORT"
- value = 6379
+ name = "REDIS_URL"
+ value = "redis://${kubernetes_service.twentycrm_redis.metadata.0.name}.${kubernetes_namespace.twentycrm.metadata.0.name}.svc.cluster.local:6379"
}
env {
name = "ENABLE_DB_MIGRATIONS"
@@ -95,7 +91,7 @@ resource "kubernetes_deployment" "twentycrm_server" {
value = "1h"
}
env {
- name = "ACCESS_TOKEN_SECRET"
+ name = "APP_SECRET"
value_from {
secret_key_ref {
name = "tokens"
@@ -104,36 +100,6 @@ resource "kubernetes_deployment" "twentycrm_server" {
}
}
- env {
- name = "LOGIN_TOKEN_SECRET"
- value_from {
- secret_key_ref {
- name = "tokens"
- key = "loginToken"
- }
- }
- }
-
- env {
- name = "REFRESH_TOKEN_SECRET"
- value_from {
- secret_key_ref {
- name = "tokens"
- key = "refreshToken"
- }
- }
- }
-
- env {
- name = "FILE_TOKEN_SECRET"
- value_from {
- secret_key_ref {
- name = "tokens"
- key = "fileToken"
- }
- }
- }
-
port {
container_port = 3000
protocol = "TCP"
diff --git a/packages/twenty-docker/k8s/terraform/deployment-worker.tf b/packages/twenty-docker/k8s/terraform/deployment-worker.tf
index 78e5ea6dcc1d..aa68fd3af2da 100644
--- a/packages/twenty-docker/k8s/terraform/deployment-worker.tf
+++ b/packages/twenty-docker/k8s/terraform/deployment-worker.tf
@@ -59,13 +59,8 @@ resource "kubernetes_deployment" "twentycrm_worker" {
}
env {
- name = "REDIS_HOST"
- value = "${kubernetes_service.twentycrm_redis.metadata.0.name}.${kubernetes_namespace.twentycrm.metadata.0.name}.svc.cluster.local"
- }
-
- env {
- name = "REDIS_PORT"
- value = 6379
+ name = "REDIS_URL"
+ value = "redis://${kubernetes_service.twentycrm_redis.metadata.0.name}.${kubernetes_namespace.twentycrm.metadata.0.name}.svc.cluster.local:6379"
}
env {
@@ -83,7 +78,7 @@ resource "kubernetes_deployment" "twentycrm_worker" {
}
env {
- name = "ACCESS_TOKEN_SECRET"
+ name = "APP_SECRET"
value_from {
secret_key_ref {
name = "tokens"
@@ -92,36 +87,6 @@ resource "kubernetes_deployment" "twentycrm_worker" {
}
}
- env {
- name = "LOGIN_TOKEN_SECRET"
- value_from {
- secret_key_ref {
- name = "tokens"
- key = "loginToken"
- }
- }
- }
-
- env {
- name = "REFRESH_TOKEN_SECRET"
- value_from {
- secret_key_ref {
- name = "tokens"
- key = "refreshToken"
- }
- }
- }
-
- env {
- name = "FILE_TOKEN_SECRET"
- value_from {
- secret_key_ref {
- name = "tokens"
- key = "fileToken"
- }
- }
- }
-
resources {
requests = {
cpu = "250m"
diff --git a/packages/twenty-docker/twenty-postgres-spilo/Dockerfile b/packages/twenty-docker/twenty-postgres-spilo/Dockerfile
index 21e107c477bb..a87a8a97ec12 100644
--- a/packages/twenty-docker/twenty-postgres-spilo/Dockerfile
+++ b/packages/twenty-docker/twenty-postgres-spilo/Dockerfile
@@ -1,6 +1,5 @@
ARG POSTGRES_VERSION=15
ARG SPILO_VERSION=3.2-p1
-ARG PG_GRAPHQL_VERSION=1.5.6
ARG WRAPPERS_VERSION=0.2.0
# Build the mysql_fdw extension
@@ -38,10 +37,9 @@ WORKDIR /build/openssl
RUN ./config && make && make install
-# Extend the Spilo image with the pg_graphql and mysql_fdw extensions
+# Extend the Spilo image with the mysql_fdw extensions
FROM ghcr.io/zalando/spilo-${POSTGRES_VERSION}:${SPILO_VERSION}
ARG POSTGRES_VERSION
-ARG PG_GRAPHQL_VERSION
ARG WRAPPERS_VERSION
ARG TARGETARCH
@@ -63,14 +61,6 @@ RUN curl -L "https://github.com/supabase/wrappers/releases/download/v${WRAPPERS_
COPY --from=build-libssl /usr/local/lib/libssl* /usr/local/lib/libcrypto* /usr/lib/
COPY --from=build-libssl /usr/local/lib/engines-1.1 /usr/lib/engines-1.1
-# Copy pg_graphql
-COPY ./packages/twenty-postgres/linux/${TARGETARCH}/${POSTGRES_VERSION}/pg_graphql/${PG_GRAPHQL_VERSION}/pg_graphql--${PG_GRAPHQL_VERSION}.sql \
- /usr/share/postgresql/${POSTGRES_VERSION}/extension
-COPY ./packages/twenty-postgres/linux/${TARGETARCH}/${POSTGRES_VERSION}/pg_graphql/${PG_GRAPHQL_VERSION}/pg_graphql.control \
- /usr/share/postgresql/${POSTGRES_VERSION}/extension
-COPY ./packages/twenty-postgres/linux/${TARGETARCH}/${POSTGRES_VERSION}/pg_graphql/${PG_GRAPHQL_VERSION}/pg_graphql.so \
- /usr/lib/postgresql/${POSTGRES_VERSION}/lib/pg_graphql.so
-
# Copy mysql_fdw
COPY --from=build-mysql_fdw /mysql_fdw/mysql_fdw.so \
/usr/lib/postgresql/${POSTGRES_VERSION}/lib/mysql_fdw.so
diff --git a/packages/twenty-docker/twenty-postgres/Dockerfile b/packages/twenty-docker/twenty-postgres/Dockerfile
index 5647a6cd35ac..9c9b96398e66 100644
--- a/packages/twenty-docker/twenty-postgres/Dockerfile
+++ b/packages/twenty-docker/twenty-postgres/Dockerfile
@@ -3,7 +3,6 @@ ARG IMAGE_TAG='15.5.0-debian-11-r15'
FROM bitnami/postgresql:${IMAGE_TAG}
ARG PG_MAIN_VERSION=15
-ARG PG_GRAPHQL_VERSION=1.5.6
ARG WRAPPERS_VERSION=0.2.0
ARG TARGETARCH
@@ -26,14 +25,6 @@ RUN set -eux; \
RUN apt update && apt install build-essential git curl default-libmysqlclient-dev -y
-# Install precompiled pg_graphql extensions
-COPY ./packages/twenty-postgres/linux/${TARGETARCH}/${PG_MAIN_VERSION}/pg_graphql/${PG_GRAPHQL_VERSION}/pg_graphql--${PG_GRAPHQL_VERSION}.sql \
- /opt/bitnami/postgresql/share/extension/
-COPY ./packages/twenty-postgres/linux/${TARGETARCH}/${PG_MAIN_VERSION}/pg_graphql/${PG_GRAPHQL_VERSION}/pg_graphql.control \
- /opt/bitnami/postgresql/share/extension/
-COPY ./packages/twenty-postgres/linux/${TARGETARCH}/${PG_MAIN_VERSION}/pg_graphql/${PG_GRAPHQL_VERSION}/pg_graphql.so \
- /opt/bitnami/postgresql/lib/
-
# Install precompiled supabase wrappers extensions
RUN curl -L "https://github.com/supabase/wrappers/releases/download/v${WRAPPERS_VERSION}/wrappers-v${WRAPPERS_VERSION}-pg${PG_MAIN_VERSION}-${TARGETARCH}-linux-gnu.deb" -o wrappers.deb
RUN dpkg --install wrappers.deb
diff --git a/packages/twenty-docker/twenty-website/Dockerfile b/packages/twenty-docker/twenty-website/Dockerfile
new file mode 100644
index 000000000000..e3b7420ff76a
--- /dev/null
+++ b/packages/twenty-docker/twenty-website/Dockerfile
@@ -0,0 +1,29 @@
+FROM node:18.17.1-alpine as twenty-website-build
+
+
+WORKDIR /app
+
+COPY ./package.json .
+COPY ./yarn.lock .
+COPY ./.yarnrc.yml .
+COPY ./.yarn/releases /app/.yarn/releases
+COPY ./tools/eslint-rules /app/tools/eslint-rules
+COPY ./packages/twenty-website/package.json /app/packages/twenty-website/package.json
+
+RUN yarn
+
+COPY ./packages/twenty-website /app/packages/twenty-website
+RUN npx nx build twenty-website
+
+FROM node:18.17.1-alpine as twenty-website
+
+WORKDIR /app/packages/twenty-website
+
+COPY --from=twenty-website-build /app /app
+
+WORKDIR /app/packages/twenty-website
+
+LABEL org.opencontainers.image.source=https://github.com/twentyhq/twenty
+LABEL org.opencontainers.image.description="This image provides a consistent and reproducible environment for the website."
+
+CMD ["/bin/sh", "-c", "npx nx start"]
\ No newline at end of file
diff --git a/packages/twenty-e2e-testing/drivers/env_variables.ts b/packages/twenty-e2e-testing/drivers/env_variables.ts
new file mode 100644
index 000000000000..2bb7f57d88fb
--- /dev/null
+++ b/packages/twenty-e2e-testing/drivers/env_variables.ts
@@ -0,0 +1,22 @@
+import * as fs from 'fs';
+import path from 'path';
+
+export const envVariables = (variables: string) => {
+ let payload = `
+ PG_DATABASE_URL=postgres://twenty:twenty@localhost:5432/default
+ FRONT_BASE_URL=http://localhost:3001
+ ACCESS_TOKEN_SECRET=replace_me_with_a_random_string_access
+ LOGIN_TOKEN_SECRET=replace_me_with_a_random_string_login
+ REFRESH_TOKEN_SECRET=replace_me_with_a_random_string_refresh
+ FILE_TOKEN_SECRET=replace_me_with_a_random_string_refresh
+ REDIS_URL=redis://localhost:6379
+ `;
+ payload = payload.concat(variables);
+ fs.writeFile(
+ path.join(__dirname, '..', '..', 'twenty-server', '.env'),
+ payload,
+ (err) => {
+ throw err;
+ },
+ );
+};
diff --git a/packages/twenty-e2e-testing/lib/pom/helper/confirmationModal.ts b/packages/twenty-e2e-testing/lib/pom/helper/confirmationModal.ts
new file mode 100644
index 000000000000..225c6733b49c
--- /dev/null
+++ b/packages/twenty-e2e-testing/lib/pom/helper/confirmationModal.ts
@@ -0,0 +1,36 @@
+import { Locator, Page } from '@playwright/test';
+
+export class ConfirmationModal {
+ private readonly input: Locator;
+ private readonly cancelButton: Locator;
+ private readonly confirmButton: Locator;
+
+ constructor(public readonly page: Page) {
+ this.page = page;
+ this.input = page.getByTestId('confirmation-modal-input');
+ this.cancelButton = page.getByRole('button', { name: 'Cancel' });
+ this.confirmButton = page.getByTestId('confirmation-modal-confirm-button');
+ }
+
+ async typePlaceholderToInput() {
+ await this.page
+ .getByTestId('confirmation-modal-input')
+ .fill(
+ await this.page
+ .getByTestId('confirmation-modal-input')
+ .getAttribute('placeholder'),
+ );
+ }
+
+ async typePhraseToInput(value: string) {
+ await this.page.getByTestId('confirmation-modal-input').fill(value);
+ }
+
+ async clickCancelButton() {
+ await this.page.getByRole('button', { name: 'Cancel' }).click();
+ }
+
+ async clickConfirmButton() {
+ await this.page.getByTestId('confirmation-modal-confirm-button').click();
+ }
+}
diff --git a/packages/twenty-e2e-testing/lib/pom/helper/formatDate.function.ts b/packages/twenty-e2e-testing/lib/pom/helper/formatDate.function.ts
new file mode 100644
index 000000000000..bffa490e80f4
--- /dev/null
+++ b/packages/twenty-e2e-testing/lib/pom/helper/formatDate.function.ts
@@ -0,0 +1,28 @@
+const nth = (d: number) => {
+ if (d > 3 && d < 21) return 'th';
+ switch (d % 10) {
+ case 1:
+ return 'st';
+ case 2:
+ return 'nd';
+ case 3:
+ return 'rd';
+ default:
+ return 'th';
+ }
+};
+
+// label looks like this: Choose Wednesday, October 30th, 2024
+// eslint-disable-next-line prefer-arrow/prefer-arrow-functions
+export function formatDate(value: string): string {
+ const date = new Date(value);
+ return 'Choose '.concat(
+ date.toLocaleDateString('en-US', { weekday: 'long' }),
+ ', ',
+ date.toLocaleDateString('en-US', { month: 'long' }),
+ ' ',
+ nth(date.getDate()),
+ ', ',
+ date.toLocaleDateString('en-US', { year: 'numeric' }),
+ );
+}
diff --git a/packages/twenty-e2e-testing/lib/pom/helper/googleLogin.ts b/packages/twenty-e2e-testing/lib/pom/helper/googleLogin.ts
new file mode 100644
index 000000000000..ca57fd6361f5
--- /dev/null
+++ b/packages/twenty-e2e-testing/lib/pom/helper/googleLogin.ts
@@ -0,0 +1,6 @@
+import { Locator, Page } from '@playwright/test';
+
+export class GoogleLogin {
+ // TODO: map all things like inputs and buttons
+ // (what's the correct way for proceeding with Google interaction? log in each time test is performed?)
+}
diff --git a/packages/twenty-e2e-testing/lib/pom/helper/iconSelect.ts b/packages/twenty-e2e-testing/lib/pom/helper/iconSelect.ts
new file mode 100644
index 000000000000..82b30c53c7af
--- /dev/null
+++ b/packages/twenty-e2e-testing/lib/pom/helper/iconSelect.ts
@@ -0,0 +1,23 @@
+import { Locator, Page } from '@playwright/test';
+
+export class IconSelect {
+ private readonly iconSelectButton: Locator;
+ private readonly iconSearchInput: Locator;
+
+ constructor(public readonly page: Page) {
+ this.iconSelectButton = page.getByLabel('Click to select icon (');
+ this.iconSearchInput = page.getByPlaceholder('Search icon');
+ }
+
+ async selectIcon(name: string) {
+ await this.iconSelectButton.click();
+ await this.iconSearchInput.fill(name);
+ await this.page.getByTitle(name).click();
+ }
+
+ async selectRelationIcon(name: string) {
+ await this.iconSelectButton.nth(1).click();
+ await this.iconSearchInput.fill(name);
+ await this.page.getByTitle(name).click();
+ }
+}
diff --git a/packages/twenty-e2e-testing/lib/pom/helper/insertFieldData.ts b/packages/twenty-e2e-testing/lib/pom/helper/insertFieldData.ts
new file mode 100644
index 000000000000..a052eff68c03
--- /dev/null
+++ b/packages/twenty-e2e-testing/lib/pom/helper/insertFieldData.ts
@@ -0,0 +1,267 @@
+import { Locator, Page } from '@playwright/test';
+import { formatDate } from './formatDate.function';
+
+export class InsertFieldData {
+ private readonly address1Input: Locator;
+ private readonly address2Input: Locator;
+ private readonly cityInput: Locator;
+ private readonly stateInput: Locator;
+ private readonly postCodeInput: Locator;
+ private readonly countrySelect: Locator;
+ private readonly arrayValueInput: Locator;
+ private readonly arrayAddValueButton: Locator;
+ // boolean react after click so no need to write special locator
+ private readonly currencySelect: Locator;
+ private readonly currencyAmountInput: Locator;
+ private readonly monthSelect: Locator;
+ private readonly yearSelect: Locator;
+ private readonly previousMonthButton: Locator;
+ private readonly nextMonthButton: Locator;
+ private readonly clearDateButton: Locator;
+ private readonly dateInput: Locator;
+ private readonly firstNameInput: Locator;
+ private readonly lastNameInput: Locator;
+ private readonly addURLButton: Locator;
+ private readonly setAsPrimaryButton: Locator;
+ private readonly addPhoneButton: Locator;
+ private readonly addMailButton: Locator;
+
+ constructor(public readonly page: Page) {
+ this.page = page;
+ this.address1Input = page.locator(
+ '//label[contains(., "ADDRESS 1")]/../div[last()]/input',
+ );
+ this.address2Input = page.locator(
+ '//label[contains(., "ADDRESS 2")]/../div[last()]/input',
+ );
+ this.cityInput = page.locator(
+ '//label[contains(., "CITY")]/../div[last()]/input',
+ );
+ this.stateInput = page.locator(
+ '//label[contains(., "STATE")]/../div[last()]/input',
+ );
+ this.postCodeInput = page.locator(
+ '//label[contains(., "POST CODE")]/../div[last()]/input',
+ );
+ this.countrySelect = page.locator(
+ '//span[contains(., "COUNTRY")]/../div[last()]/input',
+ );
+ this.arrayValueInput = page.locator("//input[@placeholder='Enter value']");
+ this.arrayAddValueButton = page.locator(
+ "//div[@data-testid='tooltip' and contains(.,'Add item')]",
+ );
+ this.currencySelect = page.locator(
+ '//body/div[last()]/div/div/div[first()]/div/div',
+ );
+ this.currencyAmountInput = page.locator("//input[@placeholder='Currency']");
+ this.monthSelect; // TODO: add once some other attributes are added
+ this.yearSelect;
+ this.previousMonthButton;
+ this.nextMonthButton;
+ this.clearDateButton = page.locator(
+ "//div[@data-testid='tooltip' and contains(., 'Clear')]",
+ );
+ this.dateInput = page.locator("//input[@placeholder='Type date and time']");
+ this.firstNameInput = page.locator("//input[@placeholder='First name']"); // may fail if placeholder is `First name` instead of `First name`
+ this.lastNameInput = page.locator("//input[@placeholder='Last name']"); // may fail if placeholder is `Last name` instead of `Last name`
+ this.addURLButton = page.locator(
+ "//div[@data-testid='tooltip' and contains(., 'Add URL')]",
+ );
+ this.setAsPrimaryButton = page.locator(
+ "//div[@data-testid='tooltip' and contains(., 'Set as primary')]",
+ );
+ this.addPhoneButton = page.locator(
+ "//div[@data-testid='tooltip' and contains(., 'Add Phone')]",
+ );
+ this.addMailButton = page.locator(
+ "//div[@data-testid='tooltip' and contains(., 'Add Email')]",
+ );
+ }
+
+ // address
+ async typeAddress1(value: string) {
+ await this.address1Input.fill(value);
+ }
+
+ async typeAddress2(value: string) {
+ await this.address2Input.fill(value);
+ }
+
+ async typeCity(value: string) {
+ await this.cityInput.fill(value);
+ }
+
+ async typeState(value: string) {
+ await this.stateInput.fill(value);
+ }
+
+ async typePostCode(value: string) {
+ await this.postCodeInput.fill(value);
+ }
+
+ async selectCountry(value: string) {
+ await this.countrySelect.click();
+ await this.page
+ .locator(`//div[@data-testid='tooltip' and contains(., '${value}')]`)
+ .click();
+ }
+
+ // array
+ async typeArrayValue(value: string) {
+ await this.arrayValueInput.fill(value);
+ }
+
+ async clickAddItemButton() {
+ await this.arrayAddValueButton.click();
+ }
+
+ // currency
+ async selectCurrency(value: string) {
+ await this.currencySelect.click();
+ await this.page
+ .locator(`//div[@data-testid='tooltip' and contains(., '${value}')]`)
+ .click();
+ }
+
+ async typeCurrencyAmount(value: string) {
+ await this.currencyAmountInput.fill(value);
+ }
+
+ // date(-time)
+ async typeDate(value: string) {
+ await this.dateInput.fill(value);
+ }
+
+ async selectMonth(value: string) {
+ await this.monthSelect.click();
+ await this.page
+ .locator(`//div[@data-testid='tooltip' and contains(., '${value}')]`)
+ .click();
+ }
+
+ async selectYear(value: string) {
+ await this.yearSelect.click();
+ await this.page
+ .locator(`//div[@data-testid='tooltip' and contains(., '${value}')]`)
+ .click();
+ }
+
+ async clickPreviousMonthButton() {
+ await this.previousMonthButton.click();
+ }
+
+ async clickNextMonthButton() {
+ await this.nextMonthButton.click();
+ }
+
+ async selectDay(value: string) {
+ await this.page
+ .locator(`//div[@aria-label='${formatDate(value)}']`)
+ .click();
+ }
+
+ async clearDate() {
+ await this.clearDateButton.click();
+ }
+
+ // email
+ async typeEmail(value: string) {
+ await this.page.locator(`//input[@placeholder='Email']`).fill(value);
+ }
+
+ async clickAddMailButton() {
+ await this.addMailButton.click();
+ }
+
+ // full name
+ async typeFirstName(name: string) {
+ await this.firstNameInput.fill(name);
+ }
+
+ async typeLastName(name: string) {
+ await this.lastNameInput.fill(name);
+ }
+
+ // JSON
+ // placeholder is dependent on the name of field
+ async typeJSON(placeholder: string, value: string) {
+ await this.page
+ .locator(`//input[@placeholder='${placeholder}']`)
+ .fill(value);
+ }
+
+ // link
+ async typeLink(value: string) {
+ await this.page.locator("//input[@placeholder='URL']").fill(value);
+ }
+
+ async clickAddURL() {
+ await this.addURLButton.click();
+ }
+
+ // (multi-)select
+ async selectValue(value: string) {
+ await this.page
+ .locator(`//div[@data-testid='tooltip' and contains(., '${value}')]`)
+ .click();
+ }
+
+ // number
+ // placeholder is dependent on the name of field
+ async typeNumber(placeholder: string, value: string) {
+ await this.page
+ .locator(`//input[@placeholder='${placeholder}']`)
+ .fill(value);
+ }
+
+ // phones
+ async selectCountryPhoneCode(countryCode: string) {
+ await this.page
+ .locator(
+ `//div[@data-testid='tooltip' and contains(., '${countryCode}')]`,
+ )
+ .click();
+ }
+
+ async typePhoneNumber(value: string) {
+ await this.page.locator(`//input[@placeholder='Phone']`).fill(value);
+ }
+
+ async clickAddPhoneButton() {
+ await this.addPhoneButton.click();
+ }
+
+ // rating
+ // if adding rating for the first time, hover must be used
+ async selectRating(rating: number) {
+ await this.page.locator(`//div[@role='slider']/div[${rating}]`).click();
+ }
+
+ // text
+ // placeholder is dependent on the name of field
+ async typeText(placeholder: string, value: string) {
+ await this.page
+ .locator(`//input[@placeholder='${placeholder}']`)
+ .fill(value);
+ }
+
+ async clickSetAsPrimaryButton() {
+ await this.setAsPrimaryButton.click();
+ }
+
+ async searchValue(value: string) {
+ await this.page.locator(`//div[@placeholder='Search']`).fill(value);
+ }
+
+ async clickEditButton() {
+ await this.page
+ .locator("//div[@data-testid='tooltip' and contains(., 'Edit')]")
+ .click();
+ }
+
+ async clickDeleteButton() {
+ await this.page
+ .locator("//div[@data-testid='tooltip' and contains(., 'Delete')]")
+ .click();
+ }
+}
diff --git a/packages/twenty-e2e-testing/lib/pom/helper/stripePage.ts b/packages/twenty-e2e-testing/lib/pom/helper/stripePage.ts
new file mode 100644
index 000000000000..ccef759f916d
--- /dev/null
+++ b/packages/twenty-e2e-testing/lib/pom/helper/stripePage.ts
@@ -0,0 +1,5 @@
+import { Locator, Page } from '@playwright/test';
+
+export class StripePage {
+ // TODO: implement all necessary methods (staging/sandbox page - does it differ anyhow from normal page?)
+}
diff --git a/packages/twenty-e2e-testing/lib/pom/helper/uploadImage.ts b/packages/twenty-e2e-testing/lib/pom/helper/uploadImage.ts
new file mode 100644
index 000000000000..41493fdc0e19
--- /dev/null
+++ b/packages/twenty-e2e-testing/lib/pom/helper/uploadImage.ts
@@ -0,0 +1,25 @@
+import { Locator, Page } from '@playwright/test';
+
+export class UploadImage {
+ private readonly imagePreview: Locator;
+ private readonly uploadButton: Locator;
+ private readonly removeButton: Locator;
+
+ constructor(public readonly page: Page) {
+ this.imagePreview = page.locator('.css-6eut39'); //TODO: add attribute to make it independent of theme
+ this.uploadButton = page.getByRole('button', { name: 'Upload' });
+ this.removeButton = page.getByRole('button', { name: 'Remove' });
+ }
+
+ async clickImagePreview() {
+ await this.imagePreview.click();
+ }
+
+ async clickUploadButton() {
+ await this.uploadButton.click();
+ }
+
+ async clickRemoveButton() {
+ await this.removeButton.click();
+ }
+}
diff --git a/packages/twenty-e2e-testing/lib/pom/leftMenu.ts b/packages/twenty-e2e-testing/lib/pom/leftMenu.ts
new file mode 100644
index 000000000000..c58c3f7f1a02
--- /dev/null
+++ b/packages/twenty-e2e-testing/lib/pom/leftMenu.ts
@@ -0,0 +1,115 @@
+import { Locator, Page } from '@playwright/test';
+
+export class LeftMenu {
+ private readonly workspaceDropdown: Locator;
+ private readonly leftMenu: Locator;
+ private readonly searchSubTab: Locator;
+ private readonly settingsTab: Locator;
+ private readonly peopleTab: Locator;
+ private readonly companiesTab: Locator;
+ private readonly opportunitiesTab: Locator;
+ private readonly opportunitiesTabAll: Locator;
+ private readonly opportunitiesTabByStage: Locator;
+ private readonly tasksTab: Locator;
+ private readonly tasksTabAll: Locator;
+ private readonly tasksTabByStatus: Locator;
+ private readonly notesTab: Locator;
+ private readonly rocketsTab: Locator;
+ private readonly workflowsTab: Locator;
+
+ constructor(public readonly page: Page) {
+ this.page = page;
+ this.workspaceDropdown = page.getByTestId('workspace-dropdown');
+ this.leftMenu = page.getByRole('button').first();
+ this.searchSubTab = page.getByText('Search');
+ this.settingsTab = page.getByRole('link', { name: 'Settings' });
+ this.peopleTab = page.getByRole('link', { name: 'People' });
+ this.companiesTab = page.getByRole('link', { name: 'Companies' });
+ this.opportunitiesTab = page.getByRole('link', { name: 'Opportunities' });
+ this.opportunitiesTabAll = page.getByRole('link', {
+ name: 'All',
+ exact: true,
+ });
+ this.opportunitiesTabByStage = page.getByRole('link', { name: 'By Stage' });
+ this.tasksTab = page.getByRole('link', { name: 'Tasks' });
+ this.tasksTabAll = page.getByRole('link', { name: 'All tasks' });
+ this.tasksTabByStatus = page.getByRole('link', { name: 'Notes' });
+ this.notesTab = page.getByRole('link', { name: 'Notes' });
+ this.rocketsTab = page.getByRole('link', { name: 'Rockets' });
+ this.workflowsTab = page.getByRole('link', { name: 'Workflows' });
+ }
+
+ async selectWorkspace(workspaceName: string) {
+ await this.workspaceDropdown.click();
+ await this.page
+ .getByTestId('tooltip')
+ .filter({ hasText: workspaceName })
+ .click();
+ }
+
+ async changeLeftMenu() {
+ await this.leftMenu.click();
+ }
+
+ async openSearchTab() {
+ await this.searchSubTab.click();
+ }
+
+ async goToSettings() {
+ await this.settingsTab.click();
+ }
+
+ async goToPeopleTab() {
+ await this.peopleTab.click();
+ }
+
+ async goToCompaniesTab() {
+ await this.companiesTab.click();
+ }
+
+ async goToOpportunitiesTab() {
+ await this.opportunitiesTab.click();
+ }
+
+ async goToOpportunitiesTableView() {
+ await this.opportunitiesTabAll.click();
+ }
+
+ async goToOpportunitiesKanbanView() {
+ await this.opportunitiesTabByStage.click();
+ }
+
+ async goToTasksTab() {
+ await this.tasksTab.click();
+ }
+
+ async goToTasksTableView() {
+ await this.tasksTabAll.click();
+ }
+
+ async goToTasksKanbanView() {
+ await this.tasksTabByStatus.click();
+ }
+
+ async goToNotesTab() {
+ await this.notesTab.click();
+ }
+
+ async goToRocketsTab() {
+ await this.rocketsTab.click();
+ }
+
+ async goToWorkflowsTab() {
+ await this.workflowsTab.click();
+ }
+
+ async goToCustomObject(customObjectName: string) {
+ await this.page.getByRole('link', { name: customObjectName }).click();
+ }
+
+ async goToCustomObjectView(name: string) {
+ await this.page.getByRole('link', { name: name }).click();
+ }
+}
+
+export default LeftMenu;
diff --git a/packages/twenty-e2e-testing/lib/pom/loginPage.ts b/packages/twenty-e2e-testing/lib/pom/loginPage.ts
new file mode 100644
index 000000000000..dc60d3f7a799
--- /dev/null
+++ b/packages/twenty-e2e-testing/lib/pom/loginPage.ts
@@ -0,0 +1,187 @@
+import { Locator, Page } from '@playwright/test';
+
+export class LoginPage {
+ private readonly loginWithGoogleButton: Locator;
+ private readonly loginWithEmailButton: Locator;
+ private readonly termsOfServiceLink: Locator;
+ private readonly privacyPolicyLink: Locator;
+ private readonly emailField: Locator;
+ private readonly continueButton: Locator;
+ private readonly forgotPasswordButton: Locator;
+ private readonly passwordField: Locator;
+ private readonly revealPasswordButton: Locator;
+ private readonly signInButton: Locator;
+ private readonly signUpButton: Locator;
+ private readonly previewImageButton: Locator;
+ private readonly uploadImageButton: Locator;
+ private readonly deleteImageButton: Locator;
+ private readonly workspaceNameField: Locator;
+ private readonly firstNameField: Locator;
+ private readonly lastNameField: Locator;
+ private readonly syncEverythingWithGoogleRadio: Locator;
+ private readonly syncSubjectAndMetadataWithGoogleRadio: Locator;
+ private readonly syncMetadataWithGoogleRadio: Locator;
+ private readonly syncWithGoogleButton: Locator;
+ private readonly noSyncButton: Locator;
+ private readonly inviteLinkField1: Locator;
+ private readonly inviteLinkField2: Locator;
+ private readonly inviteLinkField3: Locator;
+ private readonly copyInviteLink: Locator;
+ private readonly finishButton: Locator;
+
+ constructor(public readonly page: Page) {
+ this.page = page;
+ this.loginWithGoogleButton = page.getByRole('button', {
+ name: 'Continue with Google',
+ });
+ this.loginWithEmailButton = page.getByRole('button', {
+ name: 'Continue With Email',
+ });
+ this.termsOfServiceLink = page.getByRole('link', {
+ name: 'Terms of Service',
+ });
+ this.privacyPolicyLink = page.getByRole('link', { name: 'Privacy Policy' });
+ this.emailField = page.getByPlaceholder('Email');
+ this.continueButton = page.getByRole('button', {
+ name: 'Continue',
+ exact: true,
+ });
+ this.forgotPasswordButton = page.getByText('Forgot your password?');
+ this.passwordField = page.getByPlaceholder('Password');
+ this.revealPasswordButton = page.getByTestId('reveal-password-button');
+ this.signInButton = page.getByRole('button', { name: 'Sign in' });
+ this.signUpButton = page.getByRole('button', { name: 'Sign up' });
+ this.previewImageButton = page.locator('.css-1qzw107'); // TODO: fix
+ this.uploadImageButton = page.getByRole('button', { name: 'Upload' });
+ this.deleteImageButton = page.getByRole('button', { name: 'Remove' });
+ this.workspaceNameField = page.getByPlaceholder('Apple');
+ this.firstNameField = page.getByPlaceholder('Tim');
+ this.lastNameField = page.getByPlaceholder('Cook');
+ this.syncEverythingWithGoogleRadio = page.locator(
+ 'input[value="SHARE_EVERYTHING"]',
+ );
+ this.syncSubjectAndMetadataWithGoogleRadio = page.locator(
+ 'input[value="SUBJECT"]',
+ );
+ this.syncMetadataWithGoogleRadio = page.locator('input[value="METADATA"]');
+ this.syncWithGoogleButton = page.getByRole('button', {
+ name: 'Sync with Google',
+ });
+ this.noSyncButton = page.getByText('Continue without sync');
+ this.inviteLinkField1 = page.getByPlaceholder('tim@apple.dev');
+ this.inviteLinkField2 = page.getByPlaceholder('craig@apple.dev');
+ this.inviteLinkField3 = page.getByPlaceholder('mike@apple.dev');
+ this.copyInviteLink = page.getByRole('button', {
+ name: 'Copy invitation link',
+ });
+ this.finishButton = page.getByRole('button', { name: 'Finish' });
+ }
+
+ async loginWithGoogle() {
+ await this.loginWithGoogleButton.click();
+ }
+
+ async clickLoginWithEmail() {
+ await this.loginWithEmailButton.click();
+ }
+
+ async clickContinueButton() {
+ await this.continueButton.click();
+ }
+
+ async clickTermsLink() {
+ await this.termsOfServiceLink.click();
+ }
+
+ async clickPrivacyPolicyLink() {
+ await this.privacyPolicyLink.click();
+ }
+
+ async typeEmail(email: string) {
+ await this.emailField.fill(email);
+ }
+
+ async typePassword(email: string) {
+ await this.passwordField.fill(email);
+ }
+
+ async clickSignInButton() {
+ await this.signInButton.click();
+ }
+
+ async clickSignUpButton() {
+ await this.signUpButton.click();
+ }
+
+ async clickForgotPassword() {
+ await this.forgotPasswordButton.click();
+ }
+
+ async revealPassword() {
+ await this.revealPasswordButton.click();
+ }
+
+ async previewImage() {
+ await this.previewImageButton.click();
+ }
+
+ async clickUploadImage() {
+ await this.uploadImageButton.click();
+ }
+
+ async deleteImage() {
+ await this.deleteImageButton.click();
+ }
+
+ async typeWorkspaceName(workspaceName: string) {
+ await this.workspaceNameField.fill(workspaceName);
+ }
+
+ async typeFirstName(firstName: string) {
+ await this.firstNameField.fill(firstName);
+ }
+
+ async typeLastName(lastName: string) {
+ await this.lastNameField.fill(lastName);
+ }
+
+ async clickSyncEverythingWithGoogleRadio() {
+ await this.syncEverythingWithGoogleRadio.click();
+ }
+
+ async clickSyncSubjectAndMetadataWithGoogleRadio() {
+ await this.syncSubjectAndMetadataWithGoogleRadio.click();
+ }
+
+ async clickSyncMetadataWithGoogleRadio() {
+ await this.syncMetadataWithGoogleRadio.click();
+ }
+
+ async clickSyncWithGoogleButton() {
+ await this.syncWithGoogleButton.click();
+ }
+
+ async noSyncWithGoogle() {
+ await this.noSyncButton.click();
+ }
+
+ async typeInviteLink1(email: string) {
+ await this.inviteLinkField1.fill(email);
+ }
+
+ async typeInviteLink2(email: string) {
+ await this.inviteLinkField2.fill(email);
+ }
+
+ async typeInviteLink3(email: string) {
+ await this.inviteLinkField3.fill(email);
+ }
+
+ async clickCopyInviteLink() {
+ await this.copyInviteLink.click();
+ }
+
+ async clickFinishButton() {
+ await this.finishButton.click();
+ }
+}
diff --git a/packages/twenty-e2e-testing/lib/pom/mainPage.ts b/packages/twenty-e2e-testing/lib/pom/mainPage.ts
new file mode 100644
index 000000000000..a28e133b575c
--- /dev/null
+++ b/packages/twenty-e2e-testing/lib/pom/mainPage.ts
@@ -0,0 +1,196 @@
+import { Locator, Page } from '@playwright/test';
+
+export class MainPage {
+ // TODO: add missing elements (advanced filters, import/export popups)
+ private readonly tableViews: Locator;
+ private readonly addViewButton: Locator;
+ private readonly viewIconSelect: Locator;
+ private readonly viewNameInput: Locator;
+ private readonly viewTypeSelect: Locator;
+ private readonly createViewButton: Locator;
+ private readonly deleteViewButton: Locator;
+ private readonly filterButton: Locator;
+ private readonly searchFieldInput: Locator;
+ private readonly advancedFilterButton: Locator;
+ private readonly addFilterButton: Locator;
+ private readonly resetFilterButton: Locator;
+ private readonly saveFilterAsViewButton: Locator;
+ private readonly sortButton: Locator;
+ private readonly sortOrderButton: Locator;
+ private readonly optionsButton: Locator;
+ private readonly fieldsButton: Locator;
+ private readonly goBackButton: Locator;
+ private readonly hiddenFieldsButton: Locator;
+ private readonly editFieldsButton: Locator;
+ private readonly importButton: Locator;
+ private readonly exportButton: Locator;
+ private readonly deletedRecordsButton: Locator;
+ private readonly createNewRecordButton: Locator;
+ private readonly addToFavoritesButton: Locator;
+ private readonly deleteFromFavoritesButton: Locator;
+ private readonly exportBottomBarButton: Locator;
+ private readonly deleteRecordsButton: Locator;
+
+ constructor(public readonly page: Page) {
+ this.tableViews = page.getByText('·');
+ this.addViewButton = page
+ .getByTestId('tooltip')
+ .filter({ hasText: /^Add view$/ });
+ this.viewIconSelect = page.getByLabel('Click to select icon (');
+ this.viewNameInput; // can be selected using only actual value
+ this.viewTypeSelect = page.locator(
+ "//span[contains(., 'View type')]/../div",
+ );
+ this.createViewButton = page.getByRole('button', { name: 'Create' });
+ this.deleteViewButton = page.getByRole('button', { name: 'Delete' });
+ this.filterButton = page.getByText('Filter');
+ this.searchFieldInput = page.getByPlaceholder('Search fields');
+ this.advancedFilterButton = page
+ .getByTestId('tooltip')
+ .filter({ hasText: /^Advanced filter$/ });
+ this.addFilterButton = page.getByRole('button', { name: 'Add Filter' });
+ this.resetFilterButton = page.getByTestId('cancel-button');
+ this.saveFilterAsViewButton = page.getByRole('button', {
+ name: 'Save as new view',
+ });
+ this.sortButton = page.getByText('Sort');
+ this.sortOrderButton = page.locator('//li');
+ this.optionsButton = page.getByText('Options');
+ this.fieldsButton = page.getByText('Fields');
+ this.goBackButton = page.getByTestId('dropdown-menu-header-end-icon');
+ this.hiddenFieldsButton = page
+ .getByTestId('tooltip')
+ .filter({ hasText: /^Hidden Fields$/ });
+ this.editFieldsButton = page
+ .getByTestId('tooltip')
+ .filter({ hasText: /^Edit Fields$/ });
+ this.importButton = page
+ .getByTestId('tooltip')
+ .filter({ hasText: /^Import$/ });
+ this.exportButton = page
+ .getByTestId('tooltip')
+ .filter({ hasText: /^Export$/ });
+ this.deletedRecordsButton = page
+ .getByTestId('tooltip')
+ .filter({ hasText: /^Deleted */ });
+ this.createNewRecordButton = page.getByTestId('add-button');
+ this.addToFavoritesButton = page.getByText('Add to favorites');
+ this.deleteFromFavoritesButton = page.getByText('Delete from favorites');
+ this.exportBottomBarButton = page.getByText('Export');
+ this.deleteRecordsButton = page.getByText('Delete');
+ }
+
+ async clickTableViews() {
+ await this.tableViews.click();
+ }
+
+ async clickAddViewButton() {
+ await this.addViewButton.click();
+ }
+
+ async typeViewName(name: string) {
+ await this.viewNameInput.clear();
+ await this.viewNameInput.fill(name);
+ }
+
+ // name can be either be 'Table' or 'Kanban'
+ async selectViewType(name: string) {
+ await this.viewTypeSelect.click();
+ await this.page.getByTestId('tooltip').filter({ hasText: name }).click();
+ }
+
+ async createView() {
+ await this.createViewButton.click();
+ }
+
+ async deleteView() {
+ await this.deleteViewButton.click();
+ }
+
+ async clickFilterButton() {
+ await this.filterButton.click();
+ }
+
+ async searchFields(name: string) {
+ await this.searchFieldInput.clear();
+ await this.searchFieldInput.fill(name);
+ }
+
+ async clickAdvancedFilterButton() {
+ await this.advancedFilterButton.click();
+ }
+
+ async addFilter() {
+ await this.addFilterButton.click();
+ }
+
+ async resetFilter() {
+ await this.resetFilterButton.click();
+ }
+
+ async saveFilterAsView() {
+ await this.saveFilterAsViewButton.click();
+ }
+
+ async clickSortButton() {
+ await this.sortButton.click();
+ }
+
+ //can be Ascending or Descending
+ async setSortOrder(name: string) {
+ await this.sortOrderButton.click();
+ await this.page.getByTestId('tooltip').filter({ hasText: name }).click();
+ }
+
+ async clickOptionsButton() {
+ await this.optionsButton.click();
+ }
+
+ async clickFieldsButton() {
+ await this.fieldsButton.click();
+ }
+
+ async clickBackButton() {
+ await this.goBackButton.click();
+ }
+
+ async clickHiddenFieldsButton() {
+ await this.hiddenFieldsButton.click();
+ }
+
+ async clickEditFieldsButton() {
+ await this.editFieldsButton.click();
+ }
+
+ async clickImportButton() {
+ await this.importButton.click();
+ }
+
+ async clickExportButton() {
+ await this.exportButton.click();
+ }
+
+ async clickDeletedRecordsButton() {
+ await this.deletedRecordsButton.click();
+ }
+
+ async clickCreateNewRecordButton() {
+ await this.createNewRecordButton.click();
+ }
+
+ async clickAddToFavoritesButton() {
+ await this.addToFavoritesButton.click();
+ }
+
+ async clickDeleteFromFavoritesButton() {
+ await this.deleteFromFavoritesButton.click();
+ }
+
+ async clickExportBottomBarButton() {
+ await this.exportBottomBarButton.click();
+ }
+
+ async clickDeleteRecordsButton() {
+ await this.deleteRecordsButton.click();
+ }
+}
diff --git a/packages/twenty-e2e-testing/lib/pom/recordDetails.ts b/packages/twenty-e2e-testing/lib/pom/recordDetails.ts
new file mode 100644
index 000000000000..f22dd8a459b5
--- /dev/null
+++ b/packages/twenty-e2e-testing/lib/pom/recordDetails.ts
@@ -0,0 +1,150 @@
+import { Locator, Page } from '@playwright/test';
+
+export class RecordDetails {
+ // TODO: add missing components in tasks, notes, files, emails, calendar tabs
+ private readonly closeRecordButton: Locator;
+ private readonly previousRecordButton: Locator;
+ private readonly nextRecordButton: Locator;
+ private readonly favoriteRecordButton: Locator;
+ private readonly addShowPageButton: Locator;
+ private readonly moreOptionsButton: Locator;
+ private readonly deleteButton: Locator;
+ private readonly uploadProfileImageButton: Locator;
+ private readonly timelineTab: Locator;
+ private readonly tasksTab: Locator;
+ private readonly notesTab: Locator;
+ private readonly filesTab: Locator;
+ private readonly emailsTab: Locator;
+ private readonly calendarTab: Locator;
+ private readonly detachRelationButton: Locator;
+
+ constructor(public readonly page: Page) {
+ this.page = page;
+ }
+
+ async clickCloseRecordButton() {
+ await this.closeRecordButton.click();
+ }
+
+ async clickPreviousRecordButton() {
+ await this.previousRecordButton.click();
+ }
+
+ async clickNextRecordButton() {
+ await this.nextRecordButton.click();
+ }
+
+ async clickFavoriteRecordButton() {
+ await this.favoriteRecordButton.click();
+ }
+
+ async createRelatedNote() {
+ await this.addShowPageButton.click();
+ await this.page
+ .locator('//div[@data-testid="tooltip" and contains(., "Note")]')
+ .click();
+ }
+
+ async createRelatedTask() {
+ await this.addShowPageButton.click();
+ await this.page
+ .locator('//div[@data-testid="tooltip" and contains(., "Task")]')
+ .click();
+ }
+
+ async clickMoreOptionsButton() {
+ await this.moreOptionsButton.click();
+ }
+
+ async clickDeleteRecordButton() {
+ await this.deleteButton.click();
+ }
+
+ async clickUploadProfileImageButton() {
+ await this.uploadProfileImageButton.click();
+ }
+
+ async goToTimelineTab() {
+ await this.timelineTab.click();
+ }
+
+ async goToTasksTab() {
+ await this.tasksTab.click();
+ }
+
+ async goToNotesTab() {
+ await this.notesTab.click();
+ }
+
+ async goToFilesTab() {
+ await this.filesTab.click();
+ }
+
+ async goToEmailsTab() {
+ await this.emailsTab.click();
+ }
+
+ async goToCalendarTab() {
+ await this.calendarTab.click();
+ }
+
+ async clickField(name: string) {
+ await this.page
+ .locator(
+ `//div[@data-testid='tooltip' and contains(., '${name}']/../../../div[last()]/div/div`,
+ )
+ .click();
+ }
+
+ async clickFieldWithButton(name: string) {
+ await this.page
+ .locator(
+ `//div[@data-testid='tooltip' and contains(., '${name}']/../../../div[last()]/div/div`,
+ )
+ .hover();
+ await this.page
+ .locator(
+ `//div[@data-testid='tooltip' and contains(., '${name}']/../../../div[last()]/div/div[last()]/div/button`,
+ )
+ .click();
+ }
+
+ async clickRelationEditButton(name: string) {
+ await this.page.getByRole('heading').filter({ hasText: name }).hover();
+ await this.page
+ .locator(`//header[contains(., "${name}")]/div[last()]/div/button`)
+ .click();
+ }
+
+ async detachRelation(name: string) {
+ await this.page.locator(`//a[contains(., "${name}")]`).hover();
+ await this.page
+ .locator(`, //a[contains(., "${name}")]/../div[last()]/div/div/button`)
+ .hover();
+ await this.detachRelationButton.click();
+ }
+
+ async deleteRelationRecord(name: string) {
+ await this.page.locator(`//a[contains(., "${name}")]`).hover();
+ await this.page
+ .locator(`, //a[contains(., "${name}")]/../div[last()]/div/div/button`)
+ .hover();
+ await this.deleteButton.click();
+ }
+
+ async selectRelationRecord(name: string) {
+ await this.page
+ .locator(`//div[@data-testid="tooltip" and contains(., "${name}")]`)
+ .click();
+ }
+
+ async searchRelationRecord(name: string) {
+ await this.page.getByPlaceholder('Search').fill(name);
+ }
+
+ async createNewRelationRecord() {
+ await this.page
+ .locator('//div[@data-testid="tooltip" and contains(., "Add New")]')
+ .click();
+ }
+}
diff --git a/packages/twenty-e2e-testing/lib/pom/settings/accountsSection.ts b/packages/twenty-e2e-testing/lib/pom/settings/accountsSection.ts
new file mode 100644
index 000000000000..703cdffa6ed6
--- /dev/null
+++ b/packages/twenty-e2e-testing/lib/pom/settings/accountsSection.ts
@@ -0,0 +1,54 @@
+import { Locator, Page } from '@playwright/test';
+
+export class AccountsSection {
+ private readonly addAccountButton: Locator;
+ private readonly deleteAccountButton: Locator;
+ private readonly addBlocklistField: Locator;
+ private readonly addBlocklistButton: Locator;
+ private readonly connectWithGoogleButton: Locator;
+
+ constructor(public readonly page: Page) {
+ this.page = page;
+ this.addAccountButton = page.getByRole('button', { name: 'Add account' });
+ this.deleteAccountButton = page
+ .getByTestId('tooltip')
+ .getByText('Remove account');
+ this.addBlocklistField = page.getByPlaceholder(
+ 'eddy@gmail.com, @apple.com',
+ );
+ this.addBlocklistButton = page.getByRole('button', {
+ name: 'Add to blocklist',
+ });
+ this.connectWithGoogleButton = page.getByRole('button', {
+ name: 'Connect with Google',
+ });
+ }
+
+ async clickAddAccount() {
+ await this.addAccountButton.click();
+ }
+
+ async deleteAccount(email: string) {
+ await this.page
+ .locator(`//span[contains(., "${email}")]/../div/div/div/button`)
+ .click();
+ await this.deleteAccountButton.click();
+ }
+
+ async addToBlockList(domain: string) {
+ await this.addBlocklistField.fill(domain);
+ await this.addBlocklistButton.click();
+ }
+
+ async removeFromBlocklist(domain: string) {
+ await this.page
+ .locator(
+ `//div[@data-testid='tooltip' and contains(., '${domain}')]/../../div[last()]/button`,
+ )
+ .click();
+ }
+
+ async linkGoogleAccount() {
+ await this.connectWithGoogleButton.click();
+ }
+}
diff --git a/packages/twenty-e2e-testing/lib/pom/settings/calendarSection.ts b/packages/twenty-e2e-testing/lib/pom/settings/calendarSection.ts
new file mode 100644
index 000000000000..98ccba0d06f8
--- /dev/null
+++ b/packages/twenty-e2e-testing/lib/pom/settings/calendarSection.ts
@@ -0,0 +1,30 @@
+import { Locator, Page } from '@playwright/test';
+
+export class CalendarSection {
+ private readonly eventVisibilityEverythingOption: Locator;
+ private readonly eventVisibilityMetadataOption: Locator;
+ private readonly contactAutoCreation: Locator;
+
+ constructor(public readonly page: Page) {
+ this.page = page;
+ this.eventVisibilityEverythingOption = page.locator(
+ 'input[value="SHARE_EVERYTHING"]',
+ );
+ this.eventVisibilityMetadataOption = page.locator(
+ 'input[value="METADATA"]',
+ );
+ this.contactAutoCreation = page.getByRole('checkbox').nth(1);
+ }
+
+ async changeVisibilityToEverything() {
+ await this.eventVisibilityEverythingOption.click();
+ }
+
+ async changeVisibilityToMetadata() {
+ await this.eventVisibilityMetadataOption.click();
+ }
+
+ async toggleAutoCreation() {
+ await this.contactAutoCreation.click();
+ }
+}
diff --git a/packages/twenty-e2e-testing/lib/pom/settings/dataModelSection.ts b/packages/twenty-e2e-testing/lib/pom/settings/dataModelSection.ts
new file mode 100644
index 000000000000..0e36bd351d56
--- /dev/null
+++ b/packages/twenty-e2e-testing/lib/pom/settings/dataModelSection.ts
@@ -0,0 +1,189 @@
+import { Locator, Page } from '@playwright/test';
+
+export class DataModelSection {
+ private readonly searchObjectInput: Locator;
+ private readonly addObjectButton: Locator;
+ private readonly objectSingularNameInput: Locator;
+ private readonly objectPluralNameInput: Locator;
+ private readonly objectDescription: Locator;
+ private readonly synchronizeLabelAPIToggle: Locator;
+ private readonly objectAPISingularNameInput: Locator;
+ private readonly objectAPIPluralNameInput: Locator;
+ private readonly objectMoreOptionsButton: Locator;
+ private readonly editObjectButton: Locator;
+ private readonly deleteObjectButton: Locator;
+ private readonly activeSection: Locator;
+ private readonly inactiveSection: Locator;
+ private readonly searchFieldInput: Locator;
+ private readonly addFieldButton: Locator;
+ private readonly viewFieldDetailsMoreOptionsButton: Locator;
+ private readonly nameFieldInput: Locator;
+ private readonly descriptionFieldInput: Locator;
+ private readonly deactivateMoreOptionsButton: Locator;
+ private readonly activateMoreOptionsButton: Locator;
+ private readonly deactivateButton: Locator; // TODO: add attribute to make it one button
+ private readonly activateButton: Locator;
+ private readonly cancelButton: Locator;
+ private readonly saveButton: Locator;
+
+ constructor(public readonly page: Page) {
+ this.searchObjectInput = page.getByPlaceholder('Search an object...');
+ this.addObjectButton = page.getByRole('button', { name: 'Add object' });
+ this.objectSingularNameInput = page.getByPlaceholder('Listing', {
+ exact: true,
+ });
+ this.objectPluralNameInput = page.getByPlaceholder('Listings', {
+ exact: true,
+ });
+ this.objectDescription = page.getByPlaceholder('Write a description');
+ this.synchronizeLabelAPIToggle = page.getByRole('checkbox').nth(1);
+ this.objectAPISingularNameInput = page.getByPlaceholder('listing', {
+ exact: true,
+ });
+ this.objectAPIPluralNameInput = page.getByPlaceholder('listings', {
+ exact: true,
+ });
+ this.objectMoreOptionsButton = page.getByLabel('Object Options');
+ this.editObjectButton = page.getByTestId('tooltip').getByText('Edit');
+ this.deactivateMoreOptionsButton = page
+ .getByTestId('tooltip')
+ .getByText('Deactivate');
+ this.activateMoreOptionsButton = page
+ .getByTestId('tooltip')
+ .getByText('Activate');
+ this.deleteObjectButton = page.getByTestId('tooltip').getByText('Delete');
+ this.activeSection = page.getByText('Active', { exact: true });
+ this.inactiveSection = page.getByText('Inactive');
+ this.searchFieldInput = page.getByPlaceholder('Search a field...');
+ this.addFieldButton = page.getByRole('button', { name: 'Add field' });
+ this.viewFieldDetailsMoreOptionsButton = page
+ .getByTestId('tooltip')
+ .getByText('View');
+ this.nameFieldInput = page.getByPlaceholder('Employees');
+ this.descriptionFieldInput = page.getByPlaceholder('Write a description');
+ this.deactivateButton = page.getByRole('button', { name: 'Deactivate' });
+ this.activateButton = page.getByRole('button', { name: 'Activate' });
+ this.cancelButton = page.getByRole('button', { name: 'Cancel' });
+ this.saveButton = page.getByRole('button', { name: 'Save' });
+ }
+
+ async searchObject(name: string) {
+ await this.searchObjectInput.fill(name);
+ }
+
+ async clickAddObjectButton() {
+ await this.addObjectButton.click();
+ }
+
+ async typeObjectSingularName(name: string) {
+ await this.objectSingularNameInput.fill(name);
+ }
+
+ async typeObjectPluralName(name: string) {
+ await this.objectPluralNameInput.fill(name);
+ }
+
+ async typeObjectDescription(name: string) {
+ await this.objectDescription.fill(name);
+ }
+
+ async toggleSynchronizeLabelAPI() {
+ await this.synchronizeLabelAPIToggle.click();
+ }
+
+ async typeObjectSingularAPIName(name: string) {
+ await this.objectAPISingularNameInput.fill(name);
+ }
+
+ async typeObjectPluralAPIName(name: string) {
+ await this.objectAPIPluralNameInput.fill(name);
+ }
+
+ async checkObjectDetails(name: string) {
+ await this.page.getByRole('link').filter({ hasText: name }).click();
+ }
+
+ async activateInactiveObject(name: string) {
+ await this.page
+ .locator(`//div[@title="${name}"]/../../div[last()]`)
+ .click();
+ await this.activateButton.click();
+ }
+
+ // object can be deleted only if is custom and inactive
+ async deleteInactiveObject(name: string) {
+ await this.page
+ .locator(`//div[@title="${name}"]/../../div[last()]`)
+ .click();
+ await this.deleteObjectButton.click();
+ }
+
+ async editObjectDetails() {
+ await this.objectMoreOptionsButton.click();
+ await this.editObjectButton.click();
+ }
+
+ async deactivateObjectWithMoreOptions() {
+ await this.objectMoreOptionsButton.click();
+ await this.deactivateButton.click();
+ }
+
+ async searchField(name: string) {
+ await this.searchFieldInput.fill(name);
+ }
+
+ async checkFieldDetails(name: string) {
+ await this.page.locator(`//div[@title="${name}"]`).click();
+ }
+
+ async checkFieldDetailsWithButton(name: string) {
+ await this.page
+ .locator(`//div[@title="${name}"]/../../div[last()]`)
+ .click();
+ await this.viewFieldDetailsMoreOptionsButton.click();
+ }
+
+ async deactivateFieldWithButton(name: string) {
+ await this.page
+ .locator(`//div[@title="${name}"]/../../div[last()]`)
+ .click();
+ await this.deactivateMoreOptionsButton.click();
+ }
+
+ async activateFieldWithButton(name: string) {
+ await this.page
+ .locator(`//div[@title="${name}"]/../../div[last()]`)
+ .click();
+ await this.activateMoreOptionsButton.click();
+ }
+
+ async clickAddFieldButton() {
+ await this.addFieldButton.click();
+ }
+
+ async typeFieldName(name: string) {
+ await this.nameFieldInput.clear();
+ await this.nameFieldInput.fill(name);
+ }
+
+ async typeFieldDescription(description: string) {
+ await this.descriptionFieldInput.clear();
+ await this.descriptionFieldInput.fill(description);
+ }
+
+ async clickInactiveSection() {
+ await this.inactiveSection.click();
+ }
+
+ async clickActiveSection() {
+ await this.activeSection.click();
+ }
+
+ async clickCancelButton() {
+ await this.cancelButton.click();
+ }
+
+ async clickSaveButton() {
+ await this.saveButton.click();
+ }
+}
diff --git a/packages/twenty-e2e-testing/lib/pom/settings/developersSection.ts b/packages/twenty-e2e-testing/lib/pom/settings/developersSection.ts
new file mode 100644
index 000000000000..0f7fec96f697
--- /dev/null
+++ b/packages/twenty-e2e-testing/lib/pom/settings/developersSection.ts
@@ -0,0 +1,123 @@
+import { Locator, Page } from '@playwright/test';
+
+export class DevelopersSection {
+ private readonly readDocumentationButton: Locator;
+ private readonly createAPIKeyButton: Locator;
+ private readonly regenerateAPIKeyButton: Locator;
+ private readonly nameOfAPIKeyInput: Locator;
+ private readonly expirationDateAPIKeySelect: Locator;
+ private readonly createWebhookButton: Locator;
+ private readonly webhookURLInput: Locator;
+ private readonly webhookDescription: Locator;
+ private readonly webhookFilterObjectSelect: Locator;
+ private readonly webhookFilterActionSelect: Locator;
+ private readonly cancelButton: Locator;
+ private readonly saveButton: Locator;
+ private readonly deleteButton: Locator;
+
+ constructor(public readonly page: Page) {
+ this.page = page;
+ this.readDocumentationButton = page.getByRole('link', {
+ name: 'Read documentation',
+ });
+ this.createAPIKeyButton = page.getByRole('link', {
+ name: 'Create API Key',
+ });
+ this.createWebhookButton = page.getByRole('link', {
+ name: 'Create Webhook',
+ });
+ this.nameOfAPIKeyInput = page
+ .getByPlaceholder('E.g. backoffice integration')
+ .first();
+ this.expirationDateAPIKeySelect = page
+ .locator('div')
+ .filter({ hasText: /^Never$/ })
+ .nth(3); // good enough if expiration date will change only 1 time
+ this.regenerateAPIKeyButton = page.getByRole('button', {
+ name: 'Regenerate Key',
+ });
+ this.webhookURLInput = page.getByPlaceholder('URL');
+ this.webhookDescription = page.getByPlaceholder('Write a description');
+ this.webhookFilterObjectSelect = page
+ .locator('div')
+ .filter({ hasText: /^All Objects$/ })
+ .nth(3); // works only for first filter
+ this.webhookFilterActionSelect = page
+ .locator('div')
+ .filter({ hasText: /^All Actions$/ })
+ .nth(3); // works only for first filter
+ this.cancelButton = page.getByRole('button', { name: 'Cancel' });
+ this.saveButton = page.getByRole('button', { name: 'Save' });
+ this.deleteButton = page.getByRole('button', { name: 'Delete' });
+ }
+
+ async openDocumentation() {
+ await this.readDocumentationButton.click();
+ }
+
+ async createAPIKey() {
+ await this.createAPIKeyButton.click();
+ }
+
+ async typeAPIKeyName(name: string) {
+ await this.nameOfAPIKeyInput.clear();
+ await this.nameOfAPIKeyInput.fill(name);
+ }
+
+ async selectAPIExpirationDate(date: string) {
+ await this.expirationDateAPIKeySelect.click();
+ await this.page.getByText(date, { exact: true }).click();
+ }
+
+ async regenerateAPIKey() {
+ await this.regenerateAPIKeyButton.click();
+ }
+
+ async deleteAPIKey() {
+ await this.deleteButton.click();
+ }
+
+ async deleteWebhook() {
+ await this.deleteButton.click();
+ }
+
+ async createWebhook() {
+ await this.createWebhookButton.click();
+ }
+
+ async typeWebhookURL(url: string) {
+ await this.webhookURLInput.fill(url);
+ }
+
+ async typeWebhookDescription(description: string) {
+ await this.webhookDescription.fill(description);
+ }
+
+ async selectWebhookObject(object: string) {
+ // TODO: finish
+ }
+
+ async selectWebhookAction(action: string) {
+ // TODO: finish
+ }
+
+ async deleteWebhookFilter() {
+ // TODO: finish
+ }
+
+ async clickCancelButton() {
+ await this.cancelButton.click();
+ }
+
+ async clickSaveButton() {
+ await this.saveButton.click();
+ }
+
+ async checkAPIKeyDetails(name: string) {
+ await this.page.locator(`//a/div[contains(.,'${name}')][first()]`).click();
+ }
+
+ async checkWebhookDetails(name: string) {
+ await this.page.locator(`//a/div[contains(.,'${name}')][first()]`).click();
+ }
+}
diff --git a/packages/twenty-e2e-testing/lib/pom/settings/emailsSection.ts b/packages/twenty-e2e-testing/lib/pom/settings/emailsSection.ts
new file mode 100644
index 000000000000..23a83f8db07e
--- /dev/null
+++ b/packages/twenty-e2e-testing/lib/pom/settings/emailsSection.ts
@@ -0,0 +1,61 @@
+import { Locator, Page } from '@playwright/test';
+
+export class EmailsSection {
+ private readonly visibilityEverythingRadio: Locator;
+ private readonly visibilitySubjectRadio: Locator;
+ private readonly visibilityMetadataRadio: Locator;
+ private readonly autoCreationReceivedRadio: Locator;
+ private readonly autoCreationSentRadio: Locator;
+ private readonly autoCreationNoneRadio: Locator;
+ private readonly excludeNonProfessionalToggle: Locator;
+ private readonly excludeGroupToggle: Locator;
+
+ constructor(public readonly page: Page) {
+ this.page = page;
+ this.visibilityEverythingRadio = page.locator(
+ 'input[value="SHARE_EVERYTHING"]',
+ );
+ this.visibilitySubjectRadio = page.locator('input[value="SUBJECT"]');
+ this.visibilityMetadataRadio = page.locator('input[value="METADATA"]');
+ this.autoCreationReceivedRadio = page.locator(
+ 'input[value="SENT_AND_RECEIVED"]',
+ );
+ this.autoCreationSentRadio = page.locator('input[value="SENT"]');
+ this.autoCreationNoneRadio = page.locator('input[value="NONE"]');
+ // first checkbox is advanced settings toggle
+ this.excludeNonProfessionalToggle = page.getByRole('checkbox').nth(1);
+ this.excludeGroupToggle = page.getByRole('checkbox').nth(2);
+ }
+
+ async changeVisibilityToEverything() {
+ await this.visibilityEverythingRadio.click();
+ }
+
+ async changeVisibilityToSubject() {
+ await this.visibilitySubjectRadio.click();
+ }
+
+ async changeVisibilityToMetadata() {
+ await this.visibilityMetadataRadio.click();
+ }
+
+ async changeAutoCreationToAll() {
+ await this.autoCreationReceivedRadio.click();
+ }
+
+ async changeAutoCreationToSent() {
+ await this.autoCreationSentRadio.click();
+ }
+
+ async changeAutoCreationToNone() {
+ await this.autoCreationNoneRadio.click();
+ }
+
+ async toggleExcludeNonProfessional() {
+ await this.excludeNonProfessionalToggle.click();
+ }
+
+ async toggleExcludeGroup() {
+ await this.excludeGroupToggle.click();
+ }
+}
diff --git a/packages/twenty-e2e-testing/lib/pom/settings/experienceSection.ts b/packages/twenty-e2e-testing/lib/pom/settings/experienceSection.ts
new file mode 100644
index 000000000000..4ed6606c10c9
--- /dev/null
+++ b/packages/twenty-e2e-testing/lib/pom/settings/experienceSection.ts
@@ -0,0 +1,55 @@
+import { Locator, Page } from '@playwright/test';
+
+export class ExperienceSection {
+ private readonly lightThemeButton: Locator;
+ private readonly darkThemeButton: Locator;
+ private readonly timezoneDropdown: Locator;
+ private readonly dateFormatDropdown: Locator;
+ private readonly timeFormatDropdown: Locator;
+ private readonly searchInput: Locator;
+
+ constructor(public readonly page: Page) {
+ this.page = page;
+ this.lightThemeButton = page.getByText('AaLight'); // it works
+ this.darkThemeButton = page.getByText('AaDark');
+ this.timezoneDropdown = page.locator(
+ '//span[contains(., "Time zone")]/../div/div/div',
+ );
+ this.dateFormatDropdown = page.locator(
+ '//span[contains(., "Date format")]/../div/div/div',
+ );
+ this.timeFormatDropdown = page.locator(
+ '//span[contains(., "Time format")]/../div/div/div',
+ );
+ this.searchInput = page.getByPlaceholder('Search');
+ }
+
+ async changeThemeToLight() {
+ await this.lightThemeButton.click();
+ }
+
+ async changeThemeToDark() {
+ await this.darkThemeButton.click();
+ }
+
+ async selectTimeZone(timezone: string) {
+ await this.timezoneDropdown.click();
+ await this.page.getByText(timezone, { exact: true }).click();
+ }
+
+ async selectTimeZoneWithSearch(timezone: string) {
+ await this.timezoneDropdown.click();
+ await this.searchInput.fill(timezone);
+ await this.page.getByText(timezone, { exact: true }).click();
+ }
+
+ async selectDateFormat(dateFormat: string) {
+ await this.dateFormatDropdown.click();
+ await this.page.getByText(dateFormat, { exact: true }).click();
+ }
+
+ async selectTimeFormat(timeFormat: string) {
+ await this.timeFormatDropdown.click();
+ await this.page.getByText(timeFormat, { exact: true }).click();
+ }
+}
diff --git a/packages/twenty-e2e-testing/lib/pom/settings/functionsSection.ts b/packages/twenty-e2e-testing/lib/pom/settings/functionsSection.ts
new file mode 100644
index 000000000000..f8f42418a17e
--- /dev/null
+++ b/packages/twenty-e2e-testing/lib/pom/settings/functionsSection.ts
@@ -0,0 +1,159 @@
+import { Locator, Page } from '@playwright/test';
+
+export class FunctionsSection {
+ private readonly newFunctionButton: Locator;
+ private readonly functionNameInput: Locator;
+ private readonly functionDescriptionInput: Locator;
+ private readonly editorTab: Locator;
+ private readonly codeEditorField: Locator;
+ private readonly resetButton: Locator;
+ private readonly publishButton: Locator;
+ private readonly testButton: Locator;
+ private readonly testTab: Locator;
+ private readonly runFunctionButton: Locator;
+ private readonly inputField: Locator;
+ private readonly settingsTab: Locator;
+ private readonly searchVariableInput: Locator;
+ private readonly addVariableButton: Locator;
+ private readonly nameVariableInput: Locator;
+ private readonly valueVariableInput: Locator;
+ private readonly cancelVariableButton: Locator;
+ private readonly saveVariableButton: Locator;
+ private readonly editVariableButton: Locator;
+ private readonly deleteVariableButton: Locator;
+ private readonly cancelButton: Locator;
+ private readonly saveButton: Locator;
+ private readonly deleteButton: Locator;
+
+ constructor(public readonly page: Page) {
+ this.newFunctionButton = page.getByRole('button', { name: 'New Function' });
+ this.functionNameInput = page.getByPlaceholder('Name');
+ this.functionDescriptionInput = page.getByPlaceholder('Description');
+ this.editorTab = page.getByTestId('tab-editor');
+ this.codeEditorField = page.getByTestId('dummyInput'); // TODO: fix
+ this.resetButton = page.getByRole('button', { name: 'Reset' });
+ this.publishButton = page.getByRole('button', { name: 'Publish' });
+ this.testButton = page.getByRole('button', { name: 'Test' });
+ this.testTab = page.getByTestId('tab-test');
+ this.runFunctionButton = page.getByRole('button', { name: 'Run Function' });
+ this.inputField = page.getByTestId('dummyInput'); // TODO: fix
+ this.settingsTab = page.getByTestId('tab-settings');
+ this.searchVariableInput = page.getByPlaceholder('Search a variable');
+ this.addVariableButton = page.getByRole('button', { name: 'Add Variable' });
+ this.nameVariableInput = page.getByPlaceholder('Name').nth(1);
+ this.valueVariableInput = page.getByPlaceholder('Value');
+ this.cancelVariableButton = page.locator('.css-uwqduk').first(); // TODO: fix
+ this.saveVariableButton = page.locator('.css-uwqduk').nth(1); // TODO: fix
+ this.editVariableButton = page.getByText('Edit', { exact: true });
+ this.deleteVariableButton = page.getByText('Delete', { exact: true });
+ this.cancelButton = page.getByRole('button', { name: 'Cancel' });
+ this.saveButton = page.getByRole('button', { name: 'Save' });
+ this.deleteButton = page.getByRole('button', { name: 'Delete function' });
+ }
+
+ async clickNewFunction() {
+ await this.newFunctionButton.click();
+ }
+
+ async typeFunctionName(name: string) {
+ await this.functionNameInput.fill(name);
+ }
+
+ async typeFunctionDescription(description: string) {
+ await this.functionDescriptionInput.fill(description);
+ }
+
+ async checkFunctionDetails(name: string) {
+ await this.page.getByRole('link', { name: `${name} nodejs18.x` }).click();
+ }
+
+ async clickEditorTab() {
+ await this.editorTab.click();
+ }
+
+ async clickResetButton() {
+ await this.resetButton.click();
+ }
+
+ async clickPublishButton() {
+ await this.publishButton.click();
+ }
+
+ async clickTestButton() {
+ await this.testButton.click();
+ }
+
+ async typeFunctionCode() {
+ // TODO: finish once utils are merged
+ }
+
+ async clickTestTab() {
+ await this.testTab.click();
+ }
+
+ async runFunction() {
+ await this.runFunctionButton.click();
+ }
+
+ async typeFunctionInput() {
+ // TODO: finish once utils are merged
+ }
+
+ async clickSettingsTab() {
+ await this.settingsTab.click();
+ }
+
+ async searchVariable(name: string) {
+ await this.searchVariableInput.fill(name);
+ }
+
+ async addVariable() {
+ await this.addVariableButton.click();
+ }
+
+ async typeVariableName(name: string) {
+ await this.nameVariableInput.fill(name);
+ }
+
+ async typeVariableValue(value: string) {
+ await this.valueVariableInput.fill(value);
+ }
+
+ async editVariable(name: string) {
+ await this.page
+ .locator(
+ `//div[@data-testid='tooltip' and contains(., '${name}')]/../../div[last()]/div/div/button`,
+ )
+ .click();
+ await this.editVariableButton.click();
+ }
+
+ async deleteVariable(name: string) {
+ await this.page
+ .locator(
+ `//div[@data-testid='tooltip' and contains(., '${name}')]/../../div[last()]/div/div/button`,
+ )
+ .click();
+ await this.deleteVariableButton.click();
+ }
+
+ async cancelVariable() {
+ await this.cancelVariableButton.click();
+ }
+
+ async saveVariable() {
+ await this.saveVariableButton.click();
+ }
+
+ async clickCancelButton() {
+ await this.cancelButton.click();
+ }
+
+ async clickSaveButton() {
+ await this.saveButton.click();
+ }
+
+ async clickDeleteButton() {
+ await this.deleteButton.click();
+ }
+}
diff --git a/packages/twenty-e2e-testing/lib/pom/settings/generalSection.ts b/packages/twenty-e2e-testing/lib/pom/settings/generalSection.ts
new file mode 100644
index 000000000000..d936a2e9e196
--- /dev/null
+++ b/packages/twenty-e2e-testing/lib/pom/settings/generalSection.ts
@@ -0,0 +1,29 @@
+import { Locator, Page } from '@playwright/test';
+
+export class GeneralSection {
+ private readonly workspaceNameField: Locator;
+ private readonly supportSwitch: Locator;
+ private readonly deleteWorkspaceButton: Locator;
+
+ constructor(public readonly page: Page) {
+ this.page = page;
+ this.workspaceNameField = page.getByPlaceholder('Apple');
+ this.supportSwitch = page.getByRole('checkbox').nth(1);
+ this.deleteWorkspaceButton = page.getByRole('button', {
+ name: 'Delete workspace',
+ });
+ }
+
+ async changeWorkspaceName(workspaceName: string) {
+ await this.workspaceNameField.clear();
+ await this.workspaceNameField.fill(workspaceName);
+ }
+
+ async changeSupportSwitchState() {
+ await this.supportSwitch.click();
+ }
+
+ async clickDeleteWorkSpaceButton() {
+ await this.deleteWorkspaceButton.click();
+ }
+}
diff --git a/packages/twenty-e2e-testing/lib/pom/settings/membersSection.ts b/packages/twenty-e2e-testing/lib/pom/settings/membersSection.ts
new file mode 100644
index 000000000000..4f1086657028
--- /dev/null
+++ b/packages/twenty-e2e-testing/lib/pom/settings/membersSection.ts
@@ -0,0 +1,48 @@
+import { Locator, Page } from '@playwright/test';
+
+export class MembersSection {
+ private readonly inviteMembersField: Locator;
+ private readonly inviteMembersButton: Locator;
+ private readonly inviteLinkButton: Locator;
+
+ constructor(public readonly page: Page) {
+ this.page = page;
+ this.inviteMembersField = page.getByPlaceholder(
+ 'tim@apple.com, jony.ive@apple',
+ );
+ this.inviteMembersButton = page.getByRole('button', { name: 'Invite' });
+ this.inviteLinkButton = page.getByRole('button', { name: 'Copy link' });
+ }
+
+ async copyInviteLink() {
+ await this.inviteLinkButton.click();
+ }
+
+ async sendInviteEmail(email: string) {
+ await this.inviteMembersField.click();
+ await this.inviteMembersField.fill(email);
+ await this.inviteMembersButton.click();
+ }
+
+ async deleteMember(email: string) {
+ await this.page
+ .locator(`//div[contains(., '${email}')]/../../div[last()]/div/button`)
+ .click();
+ }
+
+ async deleteInviteEmail(email: string) {
+ await this.page
+ .locator(
+ `//div[contains(., '${email}')]/../../div[last()]/div/button[first()]`,
+ )
+ .click();
+ }
+
+ async refreshInviteEmail(email: string) {
+ await this.page
+ .locator(
+ `//div[contains(., '${email}')]/../../div[last()]/div/button[last()]`,
+ )
+ .click();
+ }
+}
diff --git a/packages/twenty-e2e-testing/lib/pom/settings/newFieldSection.ts b/packages/twenty-e2e-testing/lib/pom/settings/newFieldSection.ts
new file mode 100644
index 000000000000..50b37e03ac5b
--- /dev/null
+++ b/packages/twenty-e2e-testing/lib/pom/settings/newFieldSection.ts
@@ -0,0 +1,250 @@
+import { Locator, Page } from '@playwright/test';
+
+export class NewFieldSection {
+ private readonly searchTypeFieldInput: Locator;
+ private readonly currencyFieldLink: Locator;
+ private readonly currencyDefaultUnitSelect: Locator;
+ private readonly emailsFieldLink: Locator;
+ private readonly linksFieldLink: Locator;
+ private readonly phonesFieldLink: Locator;
+ private readonly addressFieldLink: Locator;
+ private readonly textFieldLink: Locator;
+ private readonly numberFieldLink: Locator;
+ private readonly decreaseDecimalsButton: Locator;
+ private readonly decimalsNumberInput: Locator;
+ private readonly increaseDecimalsButton: Locator;
+ private readonly booleanFieldLink: Locator;
+ private readonly defaultBooleanSelect: Locator;
+ private readonly dateTimeFieldLink: Locator;
+ private readonly dateFieldLink: Locator;
+ private readonly relativeDateToggle: Locator;
+ private readonly selectFieldLink: Locator;
+ private readonly multiSelectFieldLink: Locator;
+ private readonly setAsDefaultOptionButton: Locator;
+ private readonly removeOptionButton: Locator;
+ private readonly addOptionButton: Locator;
+ private readonly ratingFieldLink: Locator;
+ private readonly JSONFieldLink: Locator;
+ private readonly arrayFieldLink: Locator;
+ private readonly relationFieldLink: Locator;
+ private readonly relationTypeSelect: Locator;
+ private readonly objectDestinationSelect: Locator;
+ private readonly relationFieldNameInput: Locator;
+ private readonly fullNameFieldLink: Locator;
+ private readonly UUIDFieldLink: Locator;
+ private readonly nameFieldInput: Locator;
+ private readonly descriptionFieldInput: Locator;
+
+ constructor(public readonly page: Page) {
+ this.searchTypeFieldInput = page.getByPlaceholder('Search a type');
+ this.currencyFieldLink = page.getByRole('link', { name: 'Currency' });
+ this.currencyDefaultUnitSelect = page.locator(
+ "//span[contains(., 'Default Unit')]/../div",
+ );
+ this.emailsFieldLink = page.getByRole('link', { name: 'Emails' }).nth(1);
+ this.linksFieldLink = page.getByRole('link', { name: 'Links' });
+ this.phonesFieldLink = page.getByRole('link', { name: 'Phones' });
+ this.addressFieldLink = page.getByRole('link', { name: 'Address' });
+ this.textFieldLink = page.getByRole('link', { name: 'Text' });
+ this.numberFieldLink = page.getByRole('link', { name: 'Number' });
+ this.decreaseDecimalsButton = page.locator(
+ "//div[contains(., 'Number of decimals')]/../div[last()]/div/div/button[2]",
+ );
+ this.decimalsNumberInput = page.locator(
+ // would be better if first div was span tag
+ "//div[contains(., 'Number of decimals')]/../div[last()]/div/div/div/div/input[2]",
+ );
+ this.increaseDecimalsButton = page.locator(
+ "//div[contains(., 'Number of decimals')]/../div[last()]/div/div/button[3]",
+ );
+ this.booleanFieldLink = page.getByRole('link', { name: 'True/False' });
+ this.defaultBooleanSelect = page.locator(
+ "//span[contains(., 'Default Value')]/../div",
+ );
+ this.dateTimeFieldLink = page.getByRole('link', { name: 'Date and Time' });
+ this.dateFieldLink = page.getByRole('link', { name: 'Date' });
+ this.relativeDateToggle = page.getByRole('checkbox').nth(1);
+ this.selectFieldLink = page.getByRole('link', { name: 'Select' });
+ this.multiSelectFieldLink = page.getByRole('link', {
+ name: 'Multi-select',
+ });
+ this.setAsDefaultOptionButton = page
+ .getByTestId('tooltip')
+ .getByText('Set as default');
+ this.removeOptionButton = page
+ .getByTestId('tooltip')
+ .getByText('Remove option');
+ this.addOptionButton = page.getByRole('button', { name: 'Add option' });
+ this.ratingFieldLink = page.getByRole('link', { name: 'Rating' });
+ this.JSONFieldLink = page.getByRole('link', { name: 'JSON' });
+ this.arrayFieldLink = page.getByRole('link', { name: 'Array' });
+ this.relationFieldLink = page.getByRole('link', { name: 'Relation' });
+ this.relationTypeSelect = page.locator(
+ "//span[contains(., 'Relation type')]/../div",
+ );
+ this.objectDestinationSelect = page.locator(
+ "//span[contains(., 'Object destination')]/../div",
+ );
+ this.relationIconSelect = page.getByLabel('Click to select icon (').nth(1);
+ this.relationFieldNameInput = page.getByPlaceholder('Field name');
+ this.fullNameFieldLink = page.getByRole('link', { name: 'Full Name' });
+ this.UUIDFieldLink = page.getByRole('link', { name: 'Unique ID' });
+ this.nameFieldInput = page.getByPlaceholder('Employees');
+ this.descriptionFieldInput = page.getByPlaceholder('Write a description');
+ }
+
+ async searchTypeField(name: string) {
+ await this.searchTypeFieldInput.fill(name);
+ }
+
+ async clickCurrencyType() {
+ await this.currencyFieldLink.click();
+ }
+
+ async selectDefaultUnit(name: string) {
+ await this.currencyDefaultUnitSelect.click();
+ await this.page.getByTestId('tooltip').filter({ hasText: name }).click();
+ }
+
+ async clickEmailsType() {
+ await this.emailsFieldLink.click();
+ }
+
+ async clickLinksType() {
+ await this.linksFieldLink.click();
+ }
+
+ async clickPhonesType() {
+ await this.phonesFieldLink.click();
+ }
+
+ async clickAddressType() {
+ await this.addressFieldLink.click();
+ }
+
+ async clickTextType() {
+ await this.textFieldLink.click();
+ }
+
+ async clickNumberType() {
+ await this.numberFieldLink.click();
+ }
+
+ async decreaseDecimals() {
+ await this.decreaseDecimalsButton.click();
+ }
+
+ async typeNumberOfDecimals(amount: number) {
+ await this.decimalsNumberInput.fill(String(amount));
+ }
+
+ async increaseDecimals() {
+ await this.increaseDecimalsButton.click();
+ }
+
+ async clickBooleanType() {
+ await this.booleanFieldLink.click();
+ }
+
+ // either True of False
+ async selectDefaultBooleanValue(value: string) {
+ await this.defaultBooleanSelect.click();
+ await this.page.getByTestId('tooltip').filter({ hasText: value }).click();
+ }
+
+ async clickDateTimeType() {
+ await this.dateTimeFieldLink.click();
+ }
+
+ async clickDateType() {
+ await this.dateFieldLink.click();
+ }
+
+ async toggleRelativeDate() {
+ await this.relativeDateToggle.click();
+ }
+
+ async clickSelectType() {
+ await this.selectFieldLink.click();
+ }
+
+ async clickMultiSelectType() {
+ await this.multiSelectFieldLink.click();
+ }
+
+ async addSelectOption() {
+ await this.addOptionButton.click();
+ }
+
+ async setOptionAsDefault() {
+ // TODO: finish
+ await this.setAsDefaultOptionButton.click();
+ }
+
+ async deleteSelectOption() {
+ // TODO: finish
+ await this.removeOptionButton.click();
+ }
+
+ async changeOptionAPIName() {
+ // TODO: finish
+ }
+
+ async changeOptionColor() {
+ // TODO: finish
+ }
+
+ async changeOptionName() {
+ // TODO: finish
+ }
+
+ async clickRatingType() {
+ await this.ratingFieldLink.click();
+ }
+
+ async clickJSONType() {
+ await this.JSONFieldLink.click();
+ }
+
+ async clickArrayType() {
+ await this.arrayFieldLink.click();
+ }
+
+ async clickRelationType() {
+ await this.relationFieldLink.click();
+ }
+
+ // either 'Has many' or 'Belongs to one'
+ async selectRelationType(name: string) {
+ await this.relationTypeSelect.click();
+ await this.page.getByTestId('tooltip').filter({ hasText: name }).click();
+ }
+
+ async selectObjectDestination(name: string) {
+ await this.objectDestinationSelect.click();
+ await this.page.getByTestId('tooltip').filter({ hasText: name }).click();
+ }
+
+ async typeRelationName(name: string) {
+ await this.relationFieldNameInput.clear();
+ await this.relationFieldNameInput.fill(name);
+ }
+
+ async clickFullNameType() {
+ await this.fullNameFieldLink.click();
+ }
+
+ async clickUUIDType() {
+ await this.UUIDFieldLink.click();
+ }
+
+ async typeFieldName(name: string) {
+ await this.nameFieldInput.clear();
+ await this.nameFieldInput.fill(name);
+ }
+
+ async typeFieldDescription(description: string) {
+ await this.descriptionFieldInput.clear();
+ await this.descriptionFieldInput.fill(description);
+ }
+}
diff --git a/packages/twenty-e2e-testing/lib/pom/settings/profileSection.ts b/packages/twenty-e2e-testing/lib/pom/settings/profileSection.ts
new file mode 100644
index 000000000000..32feb9ac586c
--- /dev/null
+++ b/packages/twenty-e2e-testing/lib/pom/settings/profileSection.ts
@@ -0,0 +1,44 @@
+import { Locator, Page } from '@playwright/test';
+
+export class ProfileSection {
+ private readonly firstNameField: Locator;
+ private readonly lastNameField: Locator;
+ private readonly emailField: Locator;
+ private readonly changePasswordButton: Locator;
+ private readonly deleteAccountButton: Locator;
+
+ constructor(public readonly page: Page) {
+ this.page = page;
+ this.firstNameField = page.getByPlaceholder('Tim');
+ this.lastNameField = page.getByPlaceholder('Cook');
+ this.emailField = page.getByRole('textbox').nth(2);
+ this.changePasswordButton = page.getByRole('button', {
+ name: 'Change Password',
+ });
+ this.deleteAccountButton = page.getByRole('button', {
+ name: 'Delete account',
+ });
+ }
+
+ async changeFirstName(firstName: string) {
+ await this.firstNameField.clear();
+ await this.firstNameField.fill(firstName);
+ }
+
+ async changeLastName(lastName: string) {
+ await this.lastNameField.clear();
+ await this.lastNameField.fill(lastName);
+ }
+
+ async getEmail() {
+ await this.emailField.textContent();
+ }
+
+ async sendChangePasswordEmail() {
+ await this.changePasswordButton.click();
+ }
+
+ async deleteAccount() {
+ await this.deleteAccountButton.click();
+ }
+}
diff --git a/packages/twenty-e2e-testing/lib/pom/settings/securitySection.ts b/packages/twenty-e2e-testing/lib/pom/settings/securitySection.ts
new file mode 100644
index 000000000000..01b83b515578
--- /dev/null
+++ b/packages/twenty-e2e-testing/lib/pom/settings/securitySection.ts
@@ -0,0 +1,13 @@
+import { Locator, Page } from '@playwright/test';
+
+export class SecuritySection {
+ private readonly inviteByLinkToggle: Locator;
+
+ constructor(public readonly page: Page) {
+ this.inviteByLinkToggle = page.locator('input[type="checkbox"]').nth(1);
+ }
+
+ async toggleInviteByLink() {
+ await this.inviteByLinkToggle.click();
+ }
+}
diff --git a/packages/twenty-e2e-testing/lib/pom/settingsPage.ts b/packages/twenty-e2e-testing/lib/pom/settingsPage.ts
new file mode 100644
index 000000000000..c753bb8d0d1e
--- /dev/null
+++ b/packages/twenty-e2e-testing/lib/pom/settingsPage.ts
@@ -0,0 +1,104 @@
+import { Locator, Page } from '@playwright/test';
+
+export class SettingsPage {
+ private readonly exitSettingsLink: Locator;
+ private readonly profileLink: Locator;
+ private readonly experienceLink: Locator;
+ private readonly accountsLink: Locator;
+ private readonly emailsLink: Locator;
+ private readonly calendarsLink: Locator;
+ private readonly generalLink: Locator;
+ private readonly membersLink: Locator;
+ private readonly dataModelLink: Locator;
+ private readonly developersLink: Locator;
+ private readonly functionsLink: Locator;
+ private readonly securityLink: Locator;
+ private readonly integrationsLink: Locator;
+ private readonly releasesLink: Locator;
+ private readonly logoutLink: Locator;
+ private readonly advancedToggle: Locator;
+
+ constructor(public readonly page: Page) {
+ this.page = page;
+ this.exitSettingsLink = page.getByRole('button', { name: 'Exit Settings' });
+ this.profileLink = page.getByRole('link', { name: 'Profile' });
+ this.experienceLink = page.getByRole('link', { name: 'Experience' });
+ this.accountsLink = page.getByRole('link', { name: 'Accounts' });
+ this.emailsLink = page.getByRole('link', { name: 'Emails', exact: true });
+ this.calendarsLink = page.getByRole('link', { name: 'Calendars' });
+ this.generalLink = page.getByRole('link', { name: 'General' });
+ this.membersLink = page.getByRole('link', { name: 'Members' });
+ this.dataModelLink = page.getByRole('link', { name: 'Data model' });
+ this.developersLink = page.getByRole('link', { name: 'Developers' });
+ this.functionsLink = page.getByRole('link', { name: 'Functions' });
+ this.integrationsLink = page.getByRole('link', { name: 'Integrations' });
+ this.securityLink = page.getByRole('link', { name: 'Security' });
+ this.releasesLink = page.getByRole('link', { name: 'Releases' });
+ this.logoutLink = page.getByText('Logout');
+ this.advancedToggle = page.locator('input[type="checkbox"]').first();
+ }
+
+ async leaveSettingsPage() {
+ await this.exitSettingsLink.click();
+ }
+
+ async goToProfileSection() {
+ await this.profileLink.click();
+ }
+
+ async goToExperienceSection() {
+ await this.experienceLink.click();
+ }
+
+ async goToAccountsSection() {
+ await this.accountsLink.click();
+ }
+
+ async goToEmailsSection() {
+ await this.emailsLink.click();
+ }
+
+ async goToCalendarsSection() {
+ await this.calendarsLink.click();
+ }
+
+ async goToGeneralSection() {
+ await this.generalLink.click();
+ }
+
+ async goToMembersSection() {
+ await this.membersLink.click();
+ }
+
+ async goToDataModelSection() {
+ await this.dataModelLink.click();
+ }
+
+ async goToDevelopersSection() {
+ await this.developersLink.click();
+ }
+
+ async goToFunctionsSection() {
+ await this.functionsLink.click();
+ }
+
+ async goToSecuritySection() {
+ await this.securityLink.click();
+ }
+
+ async goToIntegrationsSection() {
+ await this.integrationsLink.click();
+ }
+
+ async goToReleasesIntegration() {
+ await this.releasesLink.click();
+ }
+
+ async logout() {
+ await this.logoutLink.click();
+ }
+
+ async toggleAdvancedSettings() {
+ await this.advancedToggle.click();
+ }
+}
diff --git a/packages/twenty-e2e-testing/lib/utils/keyboardShortcuts.ts b/packages/twenty-e2e-testing/lib/utils/keyboardShortcuts.ts
new file mode 100644
index 000000000000..470e63c1a5d8
--- /dev/null
+++ b/packages/twenty-e2e-testing/lib/utils/keyboardShortcuts.ts
@@ -0,0 +1,94 @@
+import { Page } from '@playwright/test';
+
+const MAC = process.platform === 'darwin';
+
+async function keyDownCtrlOrMeta(page: Page) {
+ if (MAC) {
+ await page.keyboard.down('Meta');
+ } else {
+ await page.keyboard.down('Control');
+ }
+}
+
+async function keyUpCtrlOrMeta(page: Page) {
+ if (MAC) {
+ await page.keyboard.up('Meta');
+ } else {
+ await page.keyboard.up('Control');
+ }
+}
+
+export async function withCtrlOrMeta(page: Page, key: () => Promise) {
+ await keyDownCtrlOrMeta(page);
+ await key();
+ await keyUpCtrlOrMeta(page);
+}
+
+export async function selectAllByKeyboard(page: Page) {
+ await keyDownCtrlOrMeta(page);
+ await page.keyboard.press('a', { delay: 50 });
+ await keyUpCtrlOrMeta(page);
+}
+
+export async function copyByKeyboard(page: Page) {
+ await keyDownCtrlOrMeta(page);
+ await page.keyboard.press('c', { delay: 50 });
+ await keyUpCtrlOrMeta(page);
+}
+
+export async function cutByKeyboard(page: Page) {
+ await keyDownCtrlOrMeta(page);
+ await page.keyboard.press('x', { delay: 50 });
+ await keyUpCtrlOrMeta(page);
+}
+
+export async function pasteByKeyboard(page: Page) {
+ await keyDownCtrlOrMeta(page);
+ await page.keyboard.press('v', { delay: 50 });
+ await keyUpCtrlOrMeta(page);
+}
+
+export async function companiesShortcut(page: Page) {
+ await page.keyboard.press('g', { delay: 50 });
+ await page.keyboard.press('c');
+}
+
+export async function notesShortcut(page: Page) {
+ await page.keyboard.press('g', { delay: 50 });
+ await page.keyboard.press('n');
+}
+
+export async function opportunitiesShortcut(page: Page) {
+ await page.keyboard.press('g', { delay: 50 });
+ await page.keyboard.press('o');
+}
+
+export async function peopleShortcut(page: Page) {
+ await page.keyboard.press('g', { delay: 50 });
+ await page.keyboard.press('p');
+}
+
+export async function rocketsShortcut(page: Page) {
+ await page.keyboard.press('g', { delay: 50 });
+ await page.keyboard.press('r');
+}
+
+export async function tasksShortcut(page: Page) {
+ await page.keyboard.press('g', { delay: 50 });
+ await page.keyboard.press('t');
+}
+
+export async function workflowsShortcut(page: Page) {
+ await page.keyboard.press('g', { delay: 50 });
+ await page.keyboard.press('w');
+}
+
+export async function settingsShortcut(page: Page) {
+ await page.keyboard.press('g', { delay: 50 });
+ await page.keyboard.press('s');
+}
+
+export async function customShortcut(page: Page, shortcut: string) {
+ await page.keyboard.press('g', { delay: 50 });
+ await page.keyboard.press(shortcut);
+}
diff --git a/packages/twenty-e2e-testing/lib/utils/pasteCodeToCodeEditor.ts b/packages/twenty-e2e-testing/lib/utils/pasteCodeToCodeEditor.ts
new file mode 100644
index 000000000000..f67defef9047
--- /dev/null
+++ b/packages/twenty-e2e-testing/lib/utils/pasteCodeToCodeEditor.ts
@@ -0,0 +1,14 @@
+import { Locator, Page } from '@playwright/test';
+import { selectAllByKeyboard } from './keyboardShortcuts';
+
+// https://github.com/microsoft/playwright/issues/14126
+// code must have \n at the end of lines otherwise everything will be in one line
+export const pasteCodeToCodeEditor = async (
+ page: Page,
+ locator: Locator,
+ code: string,
+) => {
+ await locator.click();
+ await selectAllByKeyboard(page);
+ await page.keyboard.type(code);
+};
diff --git a/packages/twenty-e2e-testing/lib/utils/uploadFile.ts b/packages/twenty-e2e-testing/lib/utils/uploadFile.ts
new file mode 100644
index 000000000000..81898bc2ba7d
--- /dev/null
+++ b/packages/twenty-e2e-testing/lib/utils/uploadFile.ts
@@ -0,0 +1,15 @@
+import { Page } from '@playwright/test';
+import path from 'path';
+
+export const fileUploader = async (
+ page: Page,
+ trigger: () => Promise,
+ filename: string,
+) => {
+ const fileChooserPromise = page.waitForEvent('filechooser');
+ await trigger();
+ const fileChooser = await fileChooserPromise;
+ await fileChooser.setFiles(
+ path.join(__dirname, '..', 'test_files', filename),
+ );
+};
diff --git a/packages/twenty-e2e-testing/tests/companies.spec.ts b/packages/twenty-e2e-testing/tests/companies.spec.ts
index b8f78c7ecad7..1aa53d61322e 100644
--- a/packages/twenty-e2e-testing/tests/companies.spec.ts
+++ b/packages/twenty-e2e-testing/tests/companies.spec.ts
@@ -1,7 +1,4 @@
import { test, expect } from '../lib/fixtures/screenshot';
-import { config } from 'dotenv';
-import path = require('path');
-config({ path: path.resolve(__dirname, '..', '.env') });
test.describe('Basic check', () => {
test('Checking if table in Companies is visible', async ({ page }) => {
diff --git a/packages/twenty-emails/package.json b/packages/twenty-emails/package.json
index 408930ac02ec..2a8354371baa 100644
--- a/packages/twenty-emails/package.json
+++ b/packages/twenty-emails/package.json
@@ -1,6 +1,6 @@
{
"name": "twenty-emails",
- "version": "0.32.0-canary",
+ "version": "0.33.0-canary",
"description": "",
"author": "",
"private": true,
diff --git a/packages/twenty-front/.env.example b/packages/twenty-front/.env.example
index 3fccb201c4b9..345d0fb92ad7 100644
--- a/packages/twenty-front/.env.example
+++ b/packages/twenty-front/.env.example
@@ -2,6 +2,7 @@ REACT_APP_SERVER_BASE_URL=http://localhost:3000
GENERATE_SOURCEMAP=false
# ———————— Optional ————————
+# REACT_APP_PORT=3001
# CHROMATIC_PROJECT_TOKEN=
# VITE_DISABLE_TYPESCRIPT_CHECKER=true
# VITE_DISABLE_ESLINT_CHECKER=true
\ No newline at end of file
diff --git a/packages/twenty-front/.eslintrc.cjs b/packages/twenty-front/.eslintrc.cjs
index df4daf7633a4..4d14adac114f 100644
--- a/packages/twenty-front/.eslintrc.cjs
+++ b/packages/twenty-front/.eslintrc.cjs
@@ -1,3 +1,5 @@
+const path = require('path');
+
module.exports = {
extends: ['../../.eslintrc.cjs', '../../.eslintrc.react.cjs'],
ignorePatterns: [
@@ -21,7 +23,16 @@ module.exports = {
parserOptions: {
project: ['packages/twenty-front/tsconfig.{json,*.json}'],
},
- rules: {},
+ plugins: ['project-structure'],
+ settings: {
+ 'project-structure/folder-structure-config-path':path.resolve(
+ __dirname,
+ 'folderStructure.json'
+ )
+ },
+ rules: {
+ 'project-structure/folder-structure': 'error',
+ },
},
],
};
diff --git a/packages/twenty-front/.gitignore b/packages/twenty-front/.gitignore
index a9e8e5b674c2..49985254bed4 100644
--- a/packages/twenty-front/.gitignore
+++ b/packages/twenty-front/.gitignore
@@ -41,4 +41,7 @@ dist-ssr
*.sw?
.vite/
-.nyc_output/
\ No newline at end of file
+.nyc_output/
+
+# eslint-plugin-project-structure
+projectStructure.cache.json
diff --git a/packages/twenty-front/.storybook/main.ts b/packages/twenty-front/.storybook/main.ts
index 8b65348c4b69..6d34593abb2c 100644
--- a/packages/twenty-front/.storybook/main.ts
+++ b/packages/twenty-front/.storybook/main.ts
@@ -50,8 +50,13 @@ const config: StorybookConfig = {
const { mergeConfig } = await import('vite');
return mergeConfig(config, {
- // Add dependencies to pre-optimization
+ resolve: {
+ alias: {
+ 'react-dom/client': 'react-dom/profiling',
+ },
+ },
});
},
+ logLevel: 'error',
};
export default config;
diff --git a/packages/twenty-front/.storybook/preview.tsx b/packages/twenty-front/.storybook/preview.tsx
index 1d67634e2a54..d35b87e856ca 100644
--- a/packages/twenty-front/.storybook/preview.tsx
+++ b/packages/twenty-front/.storybook/preview.tsx
@@ -29,6 +29,7 @@ initialize({
with payload ${JSON.stringify(requestBody)}\n
This request should be mocked with MSW`);
},
+ quiet: true,
});
const preview: Preview = {
diff --git a/packages/twenty-front/folderStructure.json b/packages/twenty-front/folderStructure.json
new file mode 100644
index 000000000000..2509807f518f
--- /dev/null
+++ b/packages/twenty-front/folderStructure.json
@@ -0,0 +1,65 @@
+{
+ "$schema": "../../node_modules/eslint-plugin-project-structure/folderStructure.schema.json",
+ "projectRoot": "packages/twenty-front",
+ "structureRoot": "src",
+ "regexParameters": {
+ "camelCase": "^[a-z]+([A-Za-z0-9]+)+",
+ "kebab-case": "[a-z][a-z0-9]*(?:-[a-z0-9]+)*"
+ },
+ "structure": [
+ { "name": "*" },
+ { "name": "*", "children": [] },
+ { "name": "modules", "ruleId": "modulesFolderRule" }
+ ],
+ "rules": {
+ "modulesFolderRule": {
+ "children": [
+ { "ruleId": "moduleFolderRule" },
+ { "name": "types", "children": [] }
+ ]
+ },
+
+ "moduleFolderRule": {
+ "name": "{kebab-case}",
+ "folderRecursionLimit": 6,
+ "children": [
+ { "ruleId": "moduleFolderRule" },
+ { "name": "hooks", "ruleId": "hooksLeafFolderRule" },
+ { "name": "utils", "ruleId": "utilsLeafFolderRule" },
+ { "name": "states", "children": [] },
+ { "name": "types", "children": [] },
+ { "name": "graphql", "children": [] },
+ { "name": "components", "children": [] },
+ { "name": "effect-components", "children": [] },
+ { "name": "constants", "children": [] },
+ { "name": "validation-schemas", "children": [] },
+ { "name": "contexts", "children": [] },
+ { "name": "scopes", "children": [] },
+ { "name": "services", "children": [] },
+ { "name": "errors", "children": [] }
+ ]
+ },
+
+ "hooksLeafFolderRule": {
+ "folderRecursionLimit": 2,
+ "children": [
+ { "name": "use{PascalCase}.(ts|tsx)" },
+ {
+ "name": "__tests__",
+ "children": [{ "name": "use{PascalCase}.test.(ts|tsx)" }]
+ },
+ { "name": "internal", "ruleId": "hooksLeafFolderRule" }
+ ]
+ },
+
+ "utilsLeafFolderRule": {
+ "children": [
+ { "name": "{camelCase}.ts" },
+ {
+ "name": "__tests__",
+ "children": [{ "name": "{camelCase}.test.ts" }]
+ }
+ ]
+ }
+ }
+}
diff --git a/packages/twenty-front/index.html b/packages/twenty-front/index.html
index 9be5c2b41f86..644e29d74fe2 100644
--- a/packages/twenty-front/index.html
+++ b/packages/twenty-front/index.html
@@ -6,7 +6,6 @@
-
Twenty
+
+
+
diff --git a/packages/twenty-front/jest.config.ts b/packages/twenty-front/jest.config.ts
index 8ed7f398db4e..ecf046e155e8 100644
--- a/packages/twenty-front/jest.config.ts
+++ b/packages/twenty-front/jest.config.ts
@@ -25,9 +25,9 @@ const jestConfig: JestConfigWithTsJest = {
extensionsToTreatAsEsm: ['.ts', '.tsx'],
coverageThreshold: {
global: {
- statements: 60,
+ statements: 58,
lines: 55,
- functions: 50,
+ functions: 47,
},
},
collectCoverageFrom: ['/src/**/*.ts'],
diff --git a/packages/twenty-front/nyc.config.cjs b/packages/twenty-front/nyc.config.cjs
index 8ae501c6910f..b4c04651ecb6 100644
--- a/packages/twenty-front/nyc.config.cjs
+++ b/packages/twenty-front/nyc.config.cjs
@@ -10,7 +10,7 @@ const modulesCoverage = {
branches: 25,
statements: 49,
lines: 50,
- functions: 40,
+ functions: 38,
include: ['src/modules/**/*'],
exclude: ['src/**/*.ts'],
};
diff --git a/packages/twenty-front/package.json b/packages/twenty-front/package.json
index 80eb86a7cf2a..ff7a7bf7bf40 100644
--- a/packages/twenty-front/package.json
+++ b/packages/twenty-front/package.json
@@ -1,6 +1,6 @@
{
"name": "twenty-front",
- "version": "0.32.0-canary",
+ "version": "0.33.0-canary",
"private": true,
"type": "module",
"scripts": {
@@ -33,6 +33,12 @@
"@nivo/calendar": "^0.87.0",
"@nivo/core": "^0.87.0",
"@nivo/line": "^0.87.0",
+ "@tiptap/extension-document": "^2.9.0",
+ "@tiptap/extension-paragraph": "^2.9.0",
+ "@tiptap/extension-placeholder": "^2.9.0",
+ "@tiptap/extension-text": "^2.9.0",
+ "@tiptap/extension-text-style": "^2.8.0",
+ "@tiptap/react": "^2.8.0",
"@xyflow/react": "^12.0.4",
"transliteration": "^2.3.5"
}
diff --git a/packages/twenty-front/project.json b/packages/twenty-front/project.json
index 3ed94b22f256..ad72457839c0 100644
--- a/packages/twenty-front/project.json
+++ b/packages/twenty-front/project.json
@@ -52,7 +52,9 @@
"reportUnusedDisableDirectives": "error"
},
"configurations": {
- "ci": { "eslintConfig": "{projectRoot}/.eslintrc-ci.cjs" },
+ "ci": {
+ "eslintConfig": "{projectRoot}/.eslintrc-ci.cjs"
+ },
"fix": {}
}
},
@@ -68,6 +70,12 @@
"storybook:build": {
"options": {
"env": { "NODE_OPTIONS": "--max_old_space_size=6500" }
+ },
+ "configurations": {
+ "docs": { "env": { "STORYBOOK_SCOPE": "ui-docs" } },
+ "modules": { "env": { "STORYBOOK_SCOPE": "modules" } },
+ "pages": { "env": { "STORYBOOK_SCOPE": "pages" } },
+ "performance": { "env": { "STORYBOOK_SCOPE": "performance" } }
}
},
"storybook:serve:dev": {
@@ -80,7 +88,13 @@
}
},
"storybook:serve:static": {
- "options": { "port": 6006 }
+ "options": { "port": 6006 },
+ "configurations": {
+ "docs": { "env": { "STORYBOOK_SCOPE": "ui-docs" } },
+ "modules": { "env": { "STORYBOOK_SCOPE": "modules" } },
+ "pages": { "env": { "STORYBOOK_SCOPE": "pages" } },
+ "performance": { "env": { "STORYBOOK_SCOPE": "performance" } }
+ }
},
"storybook:coverage": {
"configurations": {
@@ -102,9 +116,6 @@
},
"storybook:serve-and-test:static": {
"options": {
- "commands": [
- "npx concurrently --kill-others --success=first -n SB,TEST 'nx storybook:serve:static {projectName} --port={args.port}' 'npx wait-on tcp:{args.port} && nx storybook:test {projectName} --port={args.port} --configuration={args.scope}'"
- ],
"port": 6006
},
"configurations": {
@@ -114,15 +125,6 @@
"performance": { "scope": "performance" }
}
},
- "storybook:serve-and-test:static:performance": {},
- "storybook:test:no-coverage": {
- "configurations": {
- "docs": { "env": { "STORYBOOK_SCOPE": "ui-docs" } },
- "modules": { "env": { "STORYBOOK_SCOPE": "modules" } },
- "pages": { "env": { "STORYBOOK_SCOPE": "pages" } },
- "performance": { "env": { "STORYBOOK_SCOPE": "performance" } }
- }
- },
"graphql:generate": {
"executor": "nx:run-commands",
"defaultConfiguration": "data",
diff --git a/packages/twenty-front/public/logos/20-high-resolution-logo-black-transparent.png b/packages/twenty-front/public/logos/20-high-resolution-logo-black-transparent.png
new file mode 100644
index 000000000000..236da7815022
Binary files /dev/null and b/packages/twenty-front/public/logos/20-high-resolution-logo-black-transparent.png differ
diff --git a/packages/twenty-front/public/logos/20-high-resolution-logo-black.png b/packages/twenty-front/public/logos/20-high-resolution-logo-black.png
new file mode 100644
index 000000000000..db41f79831e5
Binary files /dev/null and b/packages/twenty-front/public/logos/20-high-resolution-logo-black.png differ
diff --git a/packages/twenty-front/public/logos/20-high-resolution-logo-white-transparent.png b/packages/twenty-front/public/logos/20-high-resolution-logo-white-transparent.png
new file mode 100644
index 000000000000..a7437a0f356f
Binary files /dev/null and b/packages/twenty-front/public/logos/20-high-resolution-logo-white-transparent.png differ
diff --git a/packages/twenty-front/public/logos/20-high-resolution-logo.png b/packages/twenty-front/public/logos/20-high-resolution-logo.png
new file mode 100644
index 000000000000..0dbe8de6cba1
Binary files /dev/null and b/packages/twenty-front/public/logos/20-high-resolution-logo.png differ
diff --git a/packages/twenty-front/src/__stories__/AppRouter.stories.tsx b/packages/twenty-front/src/__stories__/AppRouter.stories.tsx
index 9d2fe91a6523..f322919baeea 100644
--- a/packages/twenty-front/src/__stories__/AppRouter.stories.tsx
+++ b/packages/twenty-front/src/__stories__/AppRouter.stories.tsx
@@ -1,18 +1,18 @@
import { getOperationName } from '@apollo/client/utilities';
import { jest } from '@storybook/jest';
import { Meta, StoryObj } from '@storybook/react';
-import { graphql, HttpResponse } from 'msw';
+import { HttpResponse, graphql } from 'msw';
import { HelmetProvider } from 'react-helmet-async';
import { RecoilRoot } from 'recoil';
-import { IconsProvider } from 'twenty-ui';
import { AppErrorBoundary } from '@/error-handler/components/AppErrorBoundary';
import indexAppPath from '@/navigation/utils/indexAppPath';
-import { AppPath } from '@/types/AppPath';
import { SnackBarProviderScope } from '@/ui/feedback/snack-bar-manager/scopes/SnackBarProviderScope';
import { GET_CURRENT_USER } from '@/users/graphql/queries/getCurrentUser';
import { AppRouter } from '@/app/components/AppRouter';
+import { AppPath } from '@/types/AppPath';
+import { IconsProvider } from 'twenty-ui';
import { graphqlMocks } from '~/testing/graphqlMocks';
import { mockedUserData } from '~/testing/mock-data/users';
diff --git a/packages/twenty-front/src/generated-metadata/gql.ts b/packages/twenty-front/src/generated-metadata/gql.ts
index 415482505650..5d8e10a5ec05 100644
--- a/packages/twenty-front/src/generated-metadata/gql.ts
+++ b/packages/twenty-front/src/generated-metadata/gql.ts
@@ -32,16 +32,16 @@ const documents = {
"\n mutation DeleteOneObjectMetadataItem($idToDelete: UUID!) {\n deleteOneObject(input: { id: $idToDelete }) {\n id\n dataSourceId\n nameSingular\n namePlural\n labelSingular\n labelPlural\n description\n icon\n isCustom\n isActive\n createdAt\n updatedAt\n labelIdentifierFieldMetadataId\n imageIdentifierFieldMetadataId\n }\n }\n": types.DeleteOneObjectMetadataItemDocument,
"\n mutation DeleteOneFieldMetadataItem($idToDelete: UUID!) {\n deleteOneField(input: { id: $idToDelete }) {\n id\n type\n name\n label\n description\n icon\n isCustom\n isActive\n isNullable\n createdAt\n updatedAt\n settings\n }\n }\n": types.DeleteOneFieldMetadataItemDocument,
"\n mutation DeleteOneRelationMetadataItem($idToDelete: UUID!) {\n deleteOneRelation(input: { id: $idToDelete }) {\n id\n }\n }\n": types.DeleteOneRelationMetadataItemDocument,
- "\n query ObjectMetadataItems(\n $objectFilter: objectFilter\n $fieldFilter: fieldFilter\n ) {\n objects(paging: { first: 1000 }, filter: $objectFilter) {\n edges {\n node {\n id\n dataSourceId\n nameSingular\n namePlural\n labelSingular\n labelPlural\n description\n icon\n isCustom\n isRemote\n isActive\n isSystem\n createdAt\n updatedAt\n labelIdentifierFieldMetadataId\n imageIdentifierFieldMetadataId\n indexMetadatas(paging: { first: 100 }) {\n edges {\n node {\n id\n createdAt\n updatedAt\n name\n indexWhereClause\n indexType\n isUnique\n indexFieldMetadatas(paging: { first: 100 }) {\n edges {\n node {\n id\n createdAt\n updatedAt\n order\n fieldMetadataId\n }\n }\n }\n }\n }\n }\n fields(paging: { first: 1000 }, filter: $fieldFilter) {\n edges {\n node {\n id\n type\n name\n label\n description\n icon\n isCustom\n isActive\n isSystem\n isNullable\n isUnique\n createdAt\n updatedAt\n defaultValue\n options\n settings\n relationDefinition {\n relationId\n direction\n sourceObjectMetadata {\n id\n nameSingular\n namePlural\n }\n sourceFieldMetadata {\n id\n name\n }\n targetObjectMetadata {\n id\n nameSingular\n namePlural\n }\n targetFieldMetadata {\n id\n name\n }\n }\n }\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n }\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n }\n": types.ObjectMetadataItemsDocument,
- "\n fragment ServerlessFunctionFields on ServerlessFunction {\n id\n name\n description\n runtime\n syncStatus\n latestVersion\n createdAt\n updatedAt\n }\n": types.ServerlessFunctionFieldsFragmentDoc,
+ "\n query ObjectMetadataItems(\n $objectFilter: objectFilter\n $fieldFilter: fieldFilter\n ) {\n objects(paging: { first: 1000 }, filter: $objectFilter) {\n edges {\n node {\n id\n dataSourceId\n nameSingular\n namePlural\n labelSingular\n labelPlural\n description\n icon\n isCustom\n isRemote\n isActive\n isSystem\n createdAt\n updatedAt\n labelIdentifierFieldMetadataId\n imageIdentifierFieldMetadataId\n shortcut\n isLabelSyncedWithName\n indexMetadatas(paging: { first: 100 }) {\n edges {\n node {\n id\n createdAt\n updatedAt\n name\n indexWhereClause\n indexType\n isUnique\n indexFieldMetadatas(paging: { first: 100 }) {\n edges {\n node {\n id\n createdAt\n updatedAt\n order\n fieldMetadataId\n }\n }\n }\n }\n }\n }\n fields(paging: { first: 1000 }, filter: $fieldFilter) {\n edges {\n node {\n id\n type\n name\n label\n description\n icon\n isCustom\n isActive\n isSystem\n isNullable\n isUnique\n createdAt\n updatedAt\n defaultValue\n options\n settings\n relationDefinition {\n relationId\n direction\n sourceObjectMetadata {\n id\n nameSingular\n namePlural\n }\n sourceFieldMetadata {\n id\n name\n }\n targetObjectMetadata {\n id\n nameSingular\n namePlural\n }\n targetFieldMetadata {\n id\n name\n }\n }\n }\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n }\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n }\n": types.ObjectMetadataItemsDocument,
+ "\n fragment ServerlessFunctionFields on ServerlessFunction {\n id\n name\n description\n runtime\n syncStatus\n latestVersion\n latestVersionInputSchema\n publishedVersions\n createdAt\n updatedAt\n }\n": types.ServerlessFunctionFieldsFragmentDoc,
"\n \n mutation CreateOneServerlessFunctionItem(\n $input: CreateServerlessFunctionInput!\n ) {\n createOneServerlessFunction(input: $input) {\n ...ServerlessFunctionFields\n }\n }\n": types.CreateOneServerlessFunctionItemDocument,
- "\n \n mutation DeleteOneServerlessFunction($input: DeleteServerlessFunctionInput!) {\n deleteOneServerlessFunction(input: $input) {\n ...ServerlessFunctionFields\n }\n }\n": types.DeleteOneServerlessFunctionDocument,
+ "\n \n mutation DeleteOneServerlessFunction($input: ServerlessFunctionIdInput!) {\n deleteOneServerlessFunction(input: $input) {\n ...ServerlessFunctionFields\n }\n }\n": types.DeleteOneServerlessFunctionDocument,
"\n mutation ExecuteOneServerlessFunction(\n $input: ExecuteServerlessFunctionInput!\n ) {\n executeOneServerlessFunction(input: $input) {\n data\n duration\n status\n error\n }\n }\n": types.ExecuteOneServerlessFunctionDocument,
"\n \n mutation PublishOneServerlessFunction(\n $input: PublishServerlessFunctionInput!\n ) {\n publishServerlessFunction(input: $input) {\n ...ServerlessFunctionFields\n }\n }\n": types.PublishOneServerlessFunctionDocument,
"\n \n mutation UpdateOneServerlessFunction($input: UpdateServerlessFunctionInput!) {\n updateOneServerlessFunction(input: $input) {\n ...ServerlessFunctionFields\n }\n }\n": types.UpdateOneServerlessFunctionDocument,
"\n query FindManyAvailablePackages {\n getAvailablePackages\n }\n": types.FindManyAvailablePackagesDocument,
- "\n \n query GetManyServerlessFunctions {\n serverlessFunctions(paging: { first: 100 }) {\n edges {\n node {\n ...ServerlessFunctionFields\n }\n }\n }\n }\n": types.GetManyServerlessFunctionsDocument,
- "\n \n query GetOneServerlessFunction($id: UUID!) {\n serverlessFunction(id: $id) {\n ...ServerlessFunctionFields\n }\n }\n": types.GetOneServerlessFunctionDocument,
+ "\n \n query GetManyServerlessFunctions {\n findManyServerlessFunctions {\n ...ServerlessFunctionFields\n }\n }\n": types.GetManyServerlessFunctionsDocument,
+ "\n \n query GetOneServerlessFunction($input: ServerlessFunctionIdInput!) {\n findOneServerlessFunction(input: $input) {\n ...ServerlessFunctionFields\n }\n }\n": types.GetOneServerlessFunctionDocument,
"\n query FindOneServerlessFunctionSourceCode(\n $input: GetServerlessFunctionSourceCodeInput!\n ) {\n getServerlessFunctionSourceCode(input: $input)\n }\n": types.FindOneServerlessFunctionSourceCodeDocument,
};
@@ -138,11 +138,11 @@ export function graphql(source: "\n mutation DeleteOneRelationMetadataItem($idT
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
-export function graphql(source: "\n query ObjectMetadataItems(\n $objectFilter: objectFilter\n $fieldFilter: fieldFilter\n ) {\n objects(paging: { first: 1000 }, filter: $objectFilter) {\n edges {\n node {\n id\n dataSourceId\n nameSingular\n namePlural\n labelSingular\n labelPlural\n description\n icon\n isCustom\n isRemote\n isActive\n isSystem\n createdAt\n updatedAt\n labelIdentifierFieldMetadataId\n imageIdentifierFieldMetadataId\n indexMetadatas(paging: { first: 100 }) {\n edges {\n node {\n id\n createdAt\n updatedAt\n name\n indexWhereClause\n indexType\n isUnique\n indexFieldMetadatas(paging: { first: 100 }) {\n edges {\n node {\n id\n createdAt\n updatedAt\n order\n fieldMetadataId\n }\n }\n }\n }\n }\n }\n fields(paging: { first: 1000 }, filter: $fieldFilter) {\n edges {\n node {\n id\n type\n name\n label\n description\n icon\n isCustom\n isActive\n isSystem\n isNullable\n isUnique\n createdAt\n updatedAt\n defaultValue\n options\n settings\n relationDefinition {\n relationId\n direction\n sourceObjectMetadata {\n id\n nameSingular\n namePlural\n }\n sourceFieldMetadata {\n id\n name\n }\n targetObjectMetadata {\n id\n nameSingular\n namePlural\n }\n targetFieldMetadata {\n id\n name\n }\n }\n }\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n }\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n }\n"): (typeof documents)["\n query ObjectMetadataItems(\n $objectFilter: objectFilter\n $fieldFilter: fieldFilter\n ) {\n objects(paging: { first: 1000 }, filter: $objectFilter) {\n edges {\n node {\n id\n dataSourceId\n nameSingular\n namePlural\n labelSingular\n labelPlural\n description\n icon\n isCustom\n isRemote\n isActive\n isSystem\n createdAt\n updatedAt\n labelIdentifierFieldMetadataId\n imageIdentifierFieldMetadataId\n indexMetadatas(paging: { first: 100 }) {\n edges {\n node {\n id\n createdAt\n updatedAt\n name\n indexWhereClause\n indexType\n isUnique\n indexFieldMetadatas(paging: { first: 100 }) {\n edges {\n node {\n id\n createdAt\n updatedAt\n order\n fieldMetadataId\n }\n }\n }\n }\n }\n }\n fields(paging: { first: 1000 }, filter: $fieldFilter) {\n edges {\n node {\n id\n type\n name\n label\n description\n icon\n isCustom\n isActive\n isSystem\n isNullable\n isUnique\n createdAt\n updatedAt\n defaultValue\n options\n settings\n relationDefinition {\n relationId\n direction\n sourceObjectMetadata {\n id\n nameSingular\n namePlural\n }\n sourceFieldMetadata {\n id\n name\n }\n targetObjectMetadata {\n id\n nameSingular\n namePlural\n }\n targetFieldMetadata {\n id\n name\n }\n }\n }\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n }\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n }\n"];
+export function graphql(source: "\n query ObjectMetadataItems(\n $objectFilter: objectFilter\n $fieldFilter: fieldFilter\n ) {\n objects(paging: { first: 1000 }, filter: $objectFilter) {\n edges {\n node {\n id\n dataSourceId\n nameSingular\n namePlural\n labelSingular\n labelPlural\n description\n icon\n isCustom\n isRemote\n isActive\n isSystem\n createdAt\n updatedAt\n labelIdentifierFieldMetadataId\n imageIdentifierFieldMetadataId\n shortcut\n isLabelSyncedWithName\n indexMetadatas(paging: { first: 100 }) {\n edges {\n node {\n id\n createdAt\n updatedAt\n name\n indexWhereClause\n indexType\n isUnique\n indexFieldMetadatas(paging: { first: 100 }) {\n edges {\n node {\n id\n createdAt\n updatedAt\n order\n fieldMetadataId\n }\n }\n }\n }\n }\n }\n fields(paging: { first: 1000 }, filter: $fieldFilter) {\n edges {\n node {\n id\n type\n name\n label\n description\n icon\n isCustom\n isActive\n isSystem\n isNullable\n isUnique\n createdAt\n updatedAt\n defaultValue\n options\n settings\n relationDefinition {\n relationId\n direction\n sourceObjectMetadata {\n id\n nameSingular\n namePlural\n }\n sourceFieldMetadata {\n id\n name\n }\n targetObjectMetadata {\n id\n nameSingular\n namePlural\n }\n targetFieldMetadata {\n id\n name\n }\n }\n }\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n }\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n }\n"): (typeof documents)["\n query ObjectMetadataItems(\n $objectFilter: objectFilter\n $fieldFilter: fieldFilter\n ) {\n objects(paging: { first: 1000 }, filter: $objectFilter) {\n edges {\n node {\n id\n dataSourceId\n nameSingular\n namePlural\n labelSingular\n labelPlural\n description\n icon\n isCustom\n isRemote\n isActive\n isSystem\n createdAt\n updatedAt\n labelIdentifierFieldMetadataId\n imageIdentifierFieldMetadataId\n shortcut\n isLabelSyncedWithName\n indexMetadatas(paging: { first: 100 }) {\n edges {\n node {\n id\n createdAt\n updatedAt\n name\n indexWhereClause\n indexType\n isUnique\n indexFieldMetadatas(paging: { first: 100 }) {\n edges {\n node {\n id\n createdAt\n updatedAt\n order\n fieldMetadataId\n }\n }\n }\n }\n }\n }\n fields(paging: { first: 1000 }, filter: $fieldFilter) {\n edges {\n node {\n id\n type\n name\n label\n description\n icon\n isCustom\n isActive\n isSystem\n isNullable\n isUnique\n createdAt\n updatedAt\n defaultValue\n options\n settings\n relationDefinition {\n relationId\n direction\n sourceObjectMetadata {\n id\n nameSingular\n namePlural\n }\n sourceFieldMetadata {\n id\n name\n }\n targetObjectMetadata {\n id\n nameSingular\n namePlural\n }\n targetFieldMetadata {\n id\n name\n }\n }\n }\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n }\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
-export function graphql(source: "\n fragment ServerlessFunctionFields on ServerlessFunction {\n id\n name\n description\n runtime\n syncStatus\n latestVersion\n createdAt\n updatedAt\n }\n"): (typeof documents)["\n fragment ServerlessFunctionFields on ServerlessFunction {\n id\n name\n description\n runtime\n syncStatus\n latestVersion\n createdAt\n updatedAt\n }\n"];
+export function graphql(source: "\n fragment ServerlessFunctionFields on ServerlessFunction {\n id\n name\n description\n runtime\n syncStatus\n latestVersion\n latestVersionInputSchema\n publishedVersions\n createdAt\n updatedAt\n }\n"): (typeof documents)["\n fragment ServerlessFunctionFields on ServerlessFunction {\n id\n name\n description\n runtime\n syncStatus\n latestVersion\n latestVersionInputSchema\n publishedVersions\n createdAt\n updatedAt\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
@@ -150,7 +150,7 @@ export function graphql(source: "\n \n mutation CreateOneServerlessFunctionIte
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
-export function graphql(source: "\n \n mutation DeleteOneServerlessFunction($input: DeleteServerlessFunctionInput!) {\n deleteOneServerlessFunction(input: $input) {\n ...ServerlessFunctionFields\n }\n }\n"): (typeof documents)["\n \n mutation DeleteOneServerlessFunction($input: DeleteServerlessFunctionInput!) {\n deleteOneServerlessFunction(input: $input) {\n ...ServerlessFunctionFields\n }\n }\n"];
+export function graphql(source: "\n \n mutation DeleteOneServerlessFunction($input: ServerlessFunctionIdInput!) {\n deleteOneServerlessFunction(input: $input) {\n ...ServerlessFunctionFields\n }\n }\n"): (typeof documents)["\n \n mutation DeleteOneServerlessFunction($input: ServerlessFunctionIdInput!) {\n deleteOneServerlessFunction(input: $input) {\n ...ServerlessFunctionFields\n }\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
@@ -170,11 +170,11 @@ export function graphql(source: "\n query FindManyAvailablePackages {\n getA
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
-export function graphql(source: "\n \n query GetManyServerlessFunctions {\n serverlessFunctions(paging: { first: 100 }) {\n edges {\n node {\n ...ServerlessFunctionFields\n }\n }\n }\n }\n"): (typeof documents)["\n \n query GetManyServerlessFunctions {\n serverlessFunctions(paging: { first: 100 }) {\n edges {\n node {\n ...ServerlessFunctionFields\n }\n }\n }\n }\n"];
+export function graphql(source: "\n \n query GetManyServerlessFunctions {\n findManyServerlessFunctions {\n ...ServerlessFunctionFields\n }\n }\n"): (typeof documents)["\n \n query GetManyServerlessFunctions {\n findManyServerlessFunctions {\n ...ServerlessFunctionFields\n }\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
-export function graphql(source: "\n \n query GetOneServerlessFunction($id: UUID!) {\n serverlessFunction(id: $id) {\n ...ServerlessFunctionFields\n }\n }\n"): (typeof documents)["\n \n query GetOneServerlessFunction($id: UUID!) {\n serverlessFunction(id: $id) {\n ...ServerlessFunctionFields\n }\n }\n"];
+export function graphql(source: "\n \n query GetOneServerlessFunction($input: ServerlessFunctionIdInput!) {\n findOneServerlessFunction(input: $input) {\n ...ServerlessFunctionFields\n }\n }\n"): (typeof documents)["\n \n query GetOneServerlessFunction($input: ServerlessFunctionIdInput!) {\n findOneServerlessFunction(input: $input) {\n ...ServerlessFunctionFields\n }\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
diff --git a/packages/twenty-front/src/generated-metadata/graphql.ts b/packages/twenty-front/src/generated-metadata/graphql.ts
index 97705b937cba..b26b39ddc122 100644
--- a/packages/twenty-front/src/generated-metadata/graphql.ts
+++ b/packages/twenty-front/src/generated-metadata/graphql.ts
@@ -28,13 +28,6 @@ export type Scalars = {
Upload: { input: any; output: any; }
};
-export type AisqlQueryResult = {
- __typename?: 'AISQLQueryResult';
- queryFailedErrorMessage?: Maybe;
- sqlQuery: Scalars['String']['output'];
- sqlQueryResult?: Maybe;
-};
-
export type ActivateWorkspaceInput = {
displayName?: InputMaybe;
};
@@ -45,6 +38,16 @@ export type Analytics = {
success: Scalars['Boolean']['output'];
};
+export type AnalyticsTinybirdJwtMap = {
+ __typename?: 'AnalyticsTinybirdJwtMap';
+ getPageviewsAnalytics: Scalars['String']['output'];
+ getServerlessFunctionDuration: Scalars['String']['output'];
+ getServerlessFunctionErrorCount: Scalars['String']['output'];
+ getServerlessFunctionSuccessRate: Scalars['String']['output'];
+ getUsersAnalytics: Scalars['String']['output'];
+ getWebhookAnalytics: Scalars['String']['output'];
+};
+
export type ApiConfig = {
__typename?: 'ApiConfig';
mutationMaximumAffectedRecords: Scalars['Float']['output'];
@@ -78,6 +81,7 @@ export type AuthProviders = {
magicLink: Scalars['Boolean']['output'];
microsoft: Scalars['Boolean']['output'];
password: Scalars['Boolean']['output'];
+ sso: Scalars['Boolean']['output'];
};
export type AuthToken = {
@@ -155,6 +159,7 @@ export enum CaptchaDriverType {
export type ClientConfig = {
__typename?: 'ClientConfig';
+ analyticsEnabled: Scalars['Boolean']['output'];
api: ApiConfig;
authProviders: AuthProviders;
billing: Billing;
@@ -167,6 +172,11 @@ export type ClientConfig = {
support: Support;
};
+export type ComputeStepOutputSchemaInput = {
+ /** Step JSON format */
+ step: Scalars['JSON']['input'];
+};
+
export type CreateAppTokenInput = {
expiresAt: Scalars['DateTime']['input'];
};
@@ -193,6 +203,7 @@ export type CreateObjectInput = {
description?: InputMaybe;
icon?: InputMaybe;
imageIdentifierFieldMetadataId?: InputMaybe;
+ isLabelSyncedWithName?: InputMaybe;
isRemote?: InputMaybe;
labelIdentifierFieldMetadataId?: InputMaybe;
labelPlural: Scalars['String']['input'];
@@ -201,6 +212,7 @@ export type CreateObjectInput = {
nameSingular: Scalars['String']['input'];
primaryKeyColumnType?: InputMaybe;
primaryKeyFieldMetadataSettings?: InputMaybe;
+ shortcut?: InputMaybe;
};
export type CreateOneAppTokenInput = {
@@ -277,9 +289,13 @@ export type DeleteOneRelationInput = {
id: Scalars['UUID']['input'];
};
-export type DeleteServerlessFunctionInput = {
- /** The id of the function. */
- id: Scalars['ID']['input'];
+export type DeleteSsoInput = {
+ identityProviderId: Scalars['String']['input'];
+};
+
+export type DeleteSsoOutput = {
+ __typename?: 'DeleteSsoOutput';
+ identityProviderId: Scalars['String']['output'];
};
/** Schema update on a table */
@@ -290,6 +306,20 @@ export enum DistantTableUpdate {
TableDeleted = 'TABLE_DELETED'
}
+export type EditSsoInput = {
+ id: Scalars['String']['input'];
+ status: SsoIdentityProviderStatus;
+};
+
+export type EditSsoOutput = {
+ __typename?: 'EditSsoOutput';
+ id: Scalars['String']['output'];
+ issuer: Scalars['String']['output'];
+ name: Scalars['String']['output'];
+ status: SsoIdentityProviderStatus;
+ type: IdpType;
+};
+
export type EmailPasswordResetLink = {
__typename?: 'EmailPasswordResetLink';
/** Boolean that confirms query was dispatched */
@@ -379,6 +409,20 @@ export enum FileFolder {
WorkspaceLogo = 'WorkspaceLogo'
}
+export type FindAvailableSsoidpInput = {
+ email: Scalars['String']['input'];
+};
+
+export type FindAvailableSsoidpOutput = {
+ __typename?: 'FindAvailableSSOIDPOutput';
+ id: Scalars['String']['output'];
+ issuer: Scalars['String']['output'];
+ name: Scalars['String']['output'];
+ status: SsoIdentityProviderStatus;
+ type: IdpType;
+ workspace: WorkspaceNameAndId;
+};
+
export type FindManyRemoteTablesInput = {
/** The id of the remote server. */
id: Scalars['ID']['input'];
@@ -392,6 +436,33 @@ export type FullName = {
lastName: Scalars['String']['output'];
};
+export type GenerateJwt = GenerateJwtOutputWithAuthTokens | GenerateJwtOutputWithSsoauth;
+
+export type GenerateJwtOutputWithAuthTokens = {
+ __typename?: 'GenerateJWTOutputWithAuthTokens';
+ authTokens: AuthTokens;
+ reason: Scalars['String']['output'];
+ success: Scalars['Boolean']['output'];
+};
+
+export type GenerateJwtOutputWithSsoauth = {
+ __typename?: 'GenerateJWTOutputWithSSOAUTH';
+ availableSSOIDPs: Array;
+ reason: Scalars['String']['output'];
+ success: Scalars['Boolean']['output'];
+};
+
+export type GetAuthorizationUrlInput = {
+ identityProviderId: Scalars['String']['input'];
+};
+
+export type GetAuthorizationUrlOutput = {
+ __typename?: 'GetAuthorizationUrlOutput';
+ authorizationURL: Scalars['String']['output'];
+ id: Scalars['String']['output'];
+ type: Scalars['String']['output'];
+};
+
export type GetServerlessFunctionSourceCodeInput = {
/** The id of the function. */
id: Scalars['ID']['input'];
@@ -399,6 +470,11 @@ export type GetServerlessFunctionSourceCodeInput = {
version?: Scalars['String']['input'];
};
+export enum IdpType {
+ Oidc = 'OIDC',
+ Saml = 'SAML'
+}
+
export type IndexConnection = {
__typename?: 'IndexConnection';
/** Array of edges. */
@@ -468,12 +544,15 @@ export type Mutation = {
authorizeApp: AuthorizeApp;
challenge: LoginToken;
checkoutSession: SessionEntity;
+ computeStepOutputSchema: Scalars['JSON']['output'];
+ createOIDCIdentityProvider: SetupSsoOutput;
createOneAppToken: AppToken;
createOneField: Field;
createOneObject: Object;
createOneRelation: Relation;
createOneRemoteServer: RemoteServer;
createOneServerlessFunction: ServerlessFunction;
+ createSAMLIdentityProvider: SetupSsoOutput;
deactivateWorkflowVersion: Scalars['Boolean']['output'];
deleteCurrentWorkspace: Workspace;
deleteOneField: Field;
@@ -481,16 +560,20 @@ export type Mutation = {
deleteOneRelation: Relation;
deleteOneRemoteServer: RemoteServer;
deleteOneServerlessFunction: ServerlessFunction;
+ deleteSSOIdentityProvider: DeleteSsoOutput;
deleteUser: User;
deleteWorkspaceInvitation: Scalars['String']['output'];
disablePostgresProxy: PostgresCredentials;
+ editSSOIdentityProvider: EditSsoOutput;
emailPasswordResetLink: EmailPasswordResetLink;
enablePostgresProxy: PostgresCredentials;
exchangeAuthorizationCode: ExchangeAuthCode;
executeOneServerlessFunction: ServerlessFunctionExecutionResult;
+ findAvailableSSOIdentityProviders: Array;
generateApiKeyToken: ApiKeyToken;
- generateJWT: AuthTokens;
+ generateJWT: GenerateJwt;
generateTransientToken: TransientToken;
+ getAuthorizationUrl: GetAuthorizationUrlOutput;
impersonate: Verify;
publishServerlessFunction: ServerlessFunction;
renewToken: AuthTokens;
@@ -558,6 +641,16 @@ export type MutationCheckoutSessionArgs = {
};
+export type MutationComputeStepOutputSchemaArgs = {
+ input: ComputeStepOutputSchemaInput;
+};
+
+
+export type MutationCreateOidcIdentityProviderArgs = {
+ input: SetupOidcSsoInput;
+};
+
+
export type MutationCreateOneAppTokenArgs = {
input: CreateOneAppTokenInput;
};
@@ -588,6 +681,11 @@ export type MutationCreateOneServerlessFunctionArgs = {
};
+export type MutationCreateSamlIdentityProviderArgs = {
+ input: SetupSamlSsoInput;
+};
+
+
export type MutationDeactivateWorkflowVersionArgs = {
workflowVersionId: Scalars['String']['input'];
};
@@ -614,7 +712,12 @@ export type MutationDeleteOneRemoteServerArgs = {
export type MutationDeleteOneServerlessFunctionArgs = {
- input: DeleteServerlessFunctionInput;
+ input: ServerlessFunctionIdInput;
+};
+
+
+export type MutationDeleteSsoIdentityProviderArgs = {
+ input: DeleteSsoInput;
};
@@ -623,6 +726,11 @@ export type MutationDeleteWorkspaceInvitationArgs = {
};
+export type MutationEditSsoIdentityProviderArgs = {
+ input: EditSsoInput;
+};
+
+
export type MutationEmailPasswordResetLinkArgs = {
email: Scalars['String']['input'];
};
@@ -640,6 +748,11 @@ export type MutationExecuteOneServerlessFunctionArgs = {
};
+export type MutationFindAvailableSsoIdentityProvidersArgs = {
+ input: FindAvailableSsoidpInput;
+};
+
+
export type MutationGenerateApiKeyTokenArgs = {
apiKeyId: Scalars['String']['input'];
expiresAt: Scalars['String']['input'];
@@ -651,6 +764,11 @@ export type MutationGenerateJwtArgs = {
};
+export type MutationGetAuthorizationUrlArgs = {
+ input: GetAuthorizationUrlInput;
+};
+
+
export type MutationImpersonateArgs = {
userId: Scalars['String']['input'];
};
@@ -859,10 +977,11 @@ export type Query = {
fields: FieldConnection;
findDistantTablesWithStatus: Array;
findManyRemoteServersByType: Array;
+ findManyServerlessFunctions: Array;
findOneRemoteServerById: RemoteServer;
+ findOneServerlessFunction: ServerlessFunction;
findWorkspaceFromInviteHash: Workspace;
findWorkspaceInvitations: Array;
- getAISQLQuery: AisqlQueryResult;
getAvailablePackages: Scalars['JSON']['output'];
getPostgresCredentials?: Maybe;
getProductPrices: ProductPricesEntity;
@@ -873,12 +992,11 @@ export type Query = {
getTimelineThreadsFromPersonId: TimelineThreadsWithTotal;
index: Index;
indexMetadatas: IndexConnection;
+ listSSOIdentityProvidersByWorkspaceId: Array;
object: Object;
objects: ObjectConnection;
relation: Relation;
relations: RelationConnection;
- serverlessFunction: ServerlessFunction;
- serverlessFunctions: ServerlessFunctionConnection;
validatePasswordResetToken: ValidatePasswordResetToken;
};
@@ -925,13 +1043,13 @@ export type QueryFindOneRemoteServerByIdArgs = {
};
-export type QueryFindWorkspaceFromInviteHashArgs = {
- inviteHash: Scalars['String']['input'];
+export type QueryFindOneServerlessFunctionArgs = {
+ input: ServerlessFunctionIdInput;
};
-export type QueryGetAisqlQueryArgs = {
- text: Scalars['String']['input'];
+export type QueryFindWorkspaceFromInviteHashArgs = {
+ inviteHash: Scalars['String']['input'];
};
@@ -1005,18 +1123,6 @@ export type QueryRelationsArgs = {
};
-export type QueryServerlessFunctionArgs = {
- id: Scalars['UUID']['input'];
-};
-
-
-export type QueryServerlessFunctionsArgs = {
- filter?: ServerlessFunctionFilter;
- paging?: CursorPaging;
- sorting?: Array;
-};
-
-
export type QueryValidatePasswordResetTokenArgs = {
passwordResetToken: Scalars['String']['input'];
};
@@ -1104,6 +1210,12 @@ export type RunWorkflowVersionInput = {
workflowVersionId: Scalars['String']['input'];
};
+export enum SsoIdentityProviderStatus {
+ Active = 'Active',
+ Error = 'Error',
+ Inactive = 'Inactive'
+}
+
export type SendInvitationsOutput = {
__typename?: 'SendInvitationsOutput';
errors: Array;
@@ -1125,28 +1237,14 @@ export type ServerlessFunction = {
description?: Maybe;
id: Scalars['UUID']['output'];
latestVersion?: Maybe;
+ latestVersionInputSchema?: Maybe;
name: Scalars['String']['output'];
+ publishedVersions: Array;
runtime: Scalars['String']['output'];
syncStatus: ServerlessFunctionSyncStatus;
updatedAt: Scalars['DateTime']['output'];
};
-export type ServerlessFunctionConnection = {
- __typename?: 'ServerlessFunctionConnection';
- /** Array of edges. */
- edges: Array;
- /** Paging information */
- pageInfo: PageInfo;
-};
-
-export type ServerlessFunctionEdge = {
- __typename?: 'ServerlessFunctionEdge';
- /** Cursor for this node. */
- cursor: Scalars['ConnectionCursor']['output'];
- /** The node containing the ServerlessFunction */
- node: ServerlessFunction;
-};
-
export type ServerlessFunctionExecutionResult = {
__typename?: 'ServerlessFunctionExecutionResult';
/** Execution result in JSON format */
@@ -1165,22 +1263,11 @@ export enum ServerlessFunctionExecutionStatus {
Success = 'SUCCESS'
}
-export type ServerlessFunctionFilter = {
- and?: InputMaybe>;
- id?: InputMaybe;
- or?: InputMaybe>;
-};
-
-export type ServerlessFunctionSort = {
- direction: SortDirection;
- field: ServerlessFunctionSortFields;
- nulls?: InputMaybe;
+export type ServerlessFunctionIdInput = {
+ /** The id of the function. */
+ id: Scalars['ID']['input'];
};
-export enum ServerlessFunctionSortFields {
- Id = 'id'
-}
-
/** SyncStatus of the serverlessFunction */
export enum ServerlessFunctionSyncStatus {
NotReady = 'NOT_READY',
@@ -1192,6 +1279,31 @@ export type SessionEntity = {
url?: Maybe;
};
+export type SetupOidcSsoInput = {
+ clientID: Scalars['String']['input'];
+ clientSecret: Scalars['String']['input'];
+ issuer: Scalars['String']['input'];
+ name: Scalars['String']['input'];
+};
+
+export type SetupSamlSsoInput = {
+ certificate: Scalars['String']['input'];
+ fingerprint?: InputMaybe;
+ id: Scalars['String']['input'];
+ issuer: Scalars['String']['input'];
+ name: Scalars['String']['input'];
+ ssoURL: Scalars['String']['input'];
+};
+
+export type SetupSsoOutput = {
+ __typename?: 'SetupSsoOutput';
+ id: Scalars['String']['output'];
+ issuer: Scalars['String']['output'];
+ name: Scalars['String']['output'];
+ status: SsoIdentityProviderStatus;
+ type: IdpType;
+};
+
/** Sort Directions */
export enum SortDirection {
Asc = 'ASC',
@@ -1340,11 +1452,13 @@ export type UpdateObjectPayload = {
icon?: InputMaybe;
imageIdentifierFieldMetadataId?: InputMaybe;
isActive?: InputMaybe;
+ isLabelSyncedWithName?: InputMaybe;
labelIdentifierFieldMetadataId?: InputMaybe;
labelPlural?: InputMaybe;
labelSingular?: InputMaybe;
namePlural?: InputMaybe;
nameSingular?: InputMaybe;
+ shortcut?: InputMaybe;
};
export type UpdateOneFieldMetadataInput = {
@@ -1381,11 +1495,13 @@ export type UpdateWorkspaceInput = {
displayName?: InputMaybe;
domainName?: InputMaybe;
inviteHash?: InputMaybe;
+ isPublicInviteLinkEnabled?: InputMaybe;
logo?: InputMaybe;
};
export type User = {
__typename?: 'User';
+ analyticsTinybirdJwts?: Maybe;
canImpersonate: Scalars['Boolean']['output'];
createdAt: Scalars['DateTime']['output'];
defaultAvatarUrl?: Maybe;
@@ -1478,8 +1594,10 @@ export type Workspace = {
displayName?: Maybe;
domainName?: Maybe;
featureFlags?: Maybe>;
+ hasValidEntrepriseKey: Scalars['Boolean']['output'];
id: Scalars['UUID']['output'];
inviteHash?: Maybe;
+ isPublicInviteLinkEnabled: Scalars['Boolean']['output'];
logo?: Maybe;
metadataVersion: Scalars['Float']['output'];
updatedAt: Scalars['DateTime']['output'];
@@ -1552,6 +1670,12 @@ export enum WorkspaceMemberTimeFormatEnum {
System = 'SYSTEM'
}
+export type WorkspaceNameAndId = {
+ __typename?: 'WorkspaceNameAndId';
+ displayName?: Maybe;
+ id: Scalars['String']['output'];
+};
+
export type Field = {
__typename?: 'field';
createdAt: Scalars['DateTime']['output'];
@@ -1670,6 +1794,7 @@ export type Object = {
indexMetadatas: ObjectIndexMetadatasConnection;
isActive: Scalars['Boolean']['output'];
isCustom: Scalars['Boolean']['output'];
+ isLabelSyncedWithName: Scalars['Boolean']['output'];
isRemote: Scalars['Boolean']['output'];
isSystem: Scalars['Boolean']['output'];
labelIdentifierFieldMetadataId?: Maybe;
@@ -1677,6 +1802,7 @@ export type Object = {
labelSingular: Scalars['String']['output'];
namePlural: Scalars['String']['output'];
nameSingular: Scalars['String']['output'];
+ shortcut?: Maybe;
updatedAt: Scalars['DateTime']['output'];
};
@@ -1863,23 +1989,23 @@ export type ObjectMetadataItemsQueryVariables = Exact<{
}>;
-export type ObjectMetadataItemsQuery = { __typename?: 'Query', objects: { __typename?: 'ObjectConnection', edges: Array<{ __typename?: 'objectEdge', node: { __typename?: 'object', id: any, dataSourceId: string, nameSingular: string, namePlural: string, labelSingular: string, labelPlural: string, description?: string | null, icon?: string | null, isCustom: boolean, isRemote: boolean, isActive: boolean, isSystem: boolean, createdAt: any, updatedAt: any, labelIdentifierFieldMetadataId?: string | null, imageIdentifierFieldMetadataId?: string | null, indexMetadatas: { __typename?: 'ObjectIndexMetadatasConnection', edges: Array<{ __typename?: 'indexEdge', node: { __typename?: 'index', id: any, createdAt: any, updatedAt: any, name: string, indexWhereClause?: string | null, indexType: IndexType, isUnique: boolean, indexFieldMetadatas: { __typename?: 'IndexIndexFieldMetadatasConnection', edges: Array<{ __typename?: 'indexFieldEdge', node: { __typename?: 'indexField', id: any, createdAt: any, updatedAt: any, order: number, fieldMetadataId: any } }> } } }> }, fields: { __typename?: 'ObjectFieldsConnection', edges: Array<{ __typename?: 'fieldEdge', node: { __typename?: 'field', id: any, type: FieldMetadataType, name: string, label: string, description?: string | null, icon?: string | null, isCustom?: boolean | null, isActive?: boolean | null, isSystem?: boolean | null, isNullable?: boolean | null, isUnique?: boolean | null, createdAt: any, updatedAt: any, defaultValue?: any | null, options?: any | null, settings?: any | null, relationDefinition?: { __typename?: 'RelationDefinition', relationId: any, direction: RelationDefinitionType, sourceObjectMetadata: { __typename?: 'object', id: any, nameSingular: string, namePlural: string }, sourceFieldMetadata: { __typename?: 'field', id: any, name: string }, targetObjectMetadata: { __typename?: 'object', id: any, nameSingular: string, namePlural: string }, targetFieldMetadata: { __typename?: 'field', id: any, name: string } } | null } }>, pageInfo: { __typename?: 'PageInfo', hasNextPage?: boolean | null, hasPreviousPage?: boolean | null, startCursor?: any | null, endCursor?: any | null } } } }>, pageInfo: { __typename?: 'PageInfo', hasNextPage?: boolean | null, hasPreviousPage?: boolean | null, startCursor?: any | null, endCursor?: any | null } } };
+export type ObjectMetadataItemsQuery = { __typename?: 'Query', objects: { __typename?: 'ObjectConnection', edges: Array<{ __typename?: 'objectEdge', node: { __typename?: 'object', id: any, dataSourceId: string, nameSingular: string, namePlural: string, labelSingular: string, labelPlural: string, description?: string | null, icon?: string | null, isCustom: boolean, isRemote: boolean, isActive: boolean, isSystem: boolean, createdAt: any, updatedAt: any, labelIdentifierFieldMetadataId?: string | null, imageIdentifierFieldMetadataId?: string | null, shortcut?: string | null, isLabelSyncedWithName: boolean, indexMetadatas: { __typename?: 'ObjectIndexMetadatasConnection', edges: Array<{ __typename?: 'indexEdge', node: { __typename?: 'index', id: any, createdAt: any, updatedAt: any, name: string, indexWhereClause?: string | null, indexType: IndexType, isUnique: boolean, indexFieldMetadatas: { __typename?: 'IndexIndexFieldMetadatasConnection', edges: Array<{ __typename?: 'indexFieldEdge', node: { __typename?: 'indexField', id: any, createdAt: any, updatedAt: any, order: number, fieldMetadataId: any } }> } } }> }, fields: { __typename?: 'ObjectFieldsConnection', edges: Array<{ __typename?: 'fieldEdge', node: { __typename?: 'field', id: any, type: FieldMetadataType, name: string, label: string, description?: string | null, icon?: string | null, isCustom?: boolean | null, isActive?: boolean | null, isSystem?: boolean | null, isNullable?: boolean | null, isUnique?: boolean | null, createdAt: any, updatedAt: any, defaultValue?: any | null, options?: any | null, settings?: any | null, relationDefinition?: { __typename?: 'RelationDefinition', relationId: any, direction: RelationDefinitionType, sourceObjectMetadata: { __typename?: 'object', id: any, nameSingular: string, namePlural: string }, sourceFieldMetadata: { __typename?: 'field', id: any, name: string }, targetObjectMetadata: { __typename?: 'object', id: any, nameSingular: string, namePlural: string }, targetFieldMetadata: { __typename?: 'field', id: any, name: string } } | null } }>, pageInfo: { __typename?: 'PageInfo', hasNextPage?: boolean | null, hasPreviousPage?: boolean | null, startCursor?: any | null, endCursor?: any | null } } } }>, pageInfo: { __typename?: 'PageInfo', hasNextPage?: boolean | null, hasPreviousPage?: boolean | null, startCursor?: any | null, endCursor?: any | null } } };
-export type ServerlessFunctionFieldsFragment = { __typename?: 'ServerlessFunction', id: any, name: string, description?: string | null, runtime: string, syncStatus: ServerlessFunctionSyncStatus, latestVersion?: string | null, createdAt: any, updatedAt: any };
+export type ServerlessFunctionFieldsFragment = { __typename?: 'ServerlessFunction', id: any, name: string, description?: string | null, runtime: string, syncStatus: ServerlessFunctionSyncStatus, latestVersion?: string | null, latestVersionInputSchema?: any | null, publishedVersions: Array, createdAt: any, updatedAt: any };
export type CreateOneServerlessFunctionItemMutationVariables = Exact<{
input: CreateServerlessFunctionInput;
}>;
-export type CreateOneServerlessFunctionItemMutation = { __typename?: 'Mutation', createOneServerlessFunction: { __typename?: 'ServerlessFunction', id: any, name: string, description?: string | null, runtime: string, syncStatus: ServerlessFunctionSyncStatus, latestVersion?: string | null, createdAt: any, updatedAt: any } };
+export type CreateOneServerlessFunctionItemMutation = { __typename?: 'Mutation', createOneServerlessFunction: { __typename?: 'ServerlessFunction', id: any, name: string, description?: string | null, runtime: string, syncStatus: ServerlessFunctionSyncStatus, latestVersion?: string | null, latestVersionInputSchema?: any | null, publishedVersions: Array, createdAt: any, updatedAt: any } };
export type DeleteOneServerlessFunctionMutationVariables = Exact<{
- input: DeleteServerlessFunctionInput;
+ input: ServerlessFunctionIdInput;
}>;
-export type DeleteOneServerlessFunctionMutation = { __typename?: 'Mutation', deleteOneServerlessFunction: { __typename?: 'ServerlessFunction', id: any, name: string, description?: string | null, runtime: string, syncStatus: ServerlessFunctionSyncStatus, latestVersion?: string | null, createdAt: any, updatedAt: any } };
+export type DeleteOneServerlessFunctionMutation = { __typename?: 'Mutation', deleteOneServerlessFunction: { __typename?: 'ServerlessFunction', id: any, name: string, description?: string | null, runtime: string, syncStatus: ServerlessFunctionSyncStatus, latestVersion?: string | null, latestVersionInputSchema?: any | null, publishedVersions: Array, createdAt: any, updatedAt: any } };
export type ExecuteOneServerlessFunctionMutationVariables = Exact<{
input: ExecuteServerlessFunctionInput;
@@ -1893,14 +2019,14 @@ export type PublishOneServerlessFunctionMutationVariables = Exact<{
}>;
-export type PublishOneServerlessFunctionMutation = { __typename?: 'Mutation', publishServerlessFunction: { __typename?: 'ServerlessFunction', id: any, name: string, description?: string | null, runtime: string, syncStatus: ServerlessFunctionSyncStatus, latestVersion?: string | null, createdAt: any, updatedAt: any } };
+export type PublishOneServerlessFunctionMutation = { __typename?: 'Mutation', publishServerlessFunction: { __typename?: 'ServerlessFunction', id: any, name: string, description?: string | null, runtime: string, syncStatus: ServerlessFunctionSyncStatus, latestVersion?: string | null, latestVersionInputSchema?: any | null, publishedVersions: Array, createdAt: any, updatedAt: any } };
export type UpdateOneServerlessFunctionMutationVariables = Exact<{
input: UpdateServerlessFunctionInput;
}>;
-export type UpdateOneServerlessFunctionMutation = { __typename?: 'Mutation', updateOneServerlessFunction: { __typename?: 'ServerlessFunction', id: any, name: string, description?: string | null, runtime: string, syncStatus: ServerlessFunctionSyncStatus, latestVersion?: string | null, createdAt: any, updatedAt: any } };
+export type UpdateOneServerlessFunctionMutation = { __typename?: 'Mutation', updateOneServerlessFunction: { __typename?: 'ServerlessFunction', id: any, name: string, description?: string | null, runtime: string, syncStatus: ServerlessFunctionSyncStatus, latestVersion?: string | null, latestVersionInputSchema?: any | null, publishedVersions: Array, createdAt: any, updatedAt: any } };
export type FindManyAvailablePackagesQueryVariables = Exact<{ [key: string]: never; }>;
@@ -1910,14 +2036,14 @@ export type FindManyAvailablePackagesQuery = { __typename?: 'Query', getAvailabl
export type GetManyServerlessFunctionsQueryVariables = Exact<{ [key: string]: never; }>;
-export type GetManyServerlessFunctionsQuery = { __typename?: 'Query', serverlessFunctions: { __typename?: 'ServerlessFunctionConnection', edges: Array<{ __typename?: 'ServerlessFunctionEdge', node: { __typename?: 'ServerlessFunction', id: any, name: string, description?: string | null, runtime: string, syncStatus: ServerlessFunctionSyncStatus, latestVersion?: string | null, createdAt: any, updatedAt: any } }> } };
+export type GetManyServerlessFunctionsQuery = { __typename?: 'Query', findManyServerlessFunctions: Array<{ __typename?: 'ServerlessFunction', id: any, name: string, description?: string | null, runtime: string, syncStatus: ServerlessFunctionSyncStatus, latestVersion?: string | null, latestVersionInputSchema?: any | null, publishedVersions: Array, createdAt: any, updatedAt: any }> };
export type GetOneServerlessFunctionQueryVariables = Exact<{
- id: Scalars['UUID']['input'];
+ input: ServerlessFunctionIdInput;
}>;
-export type GetOneServerlessFunctionQuery = { __typename?: 'Query', serverlessFunction: { __typename?: 'ServerlessFunction', id: any, name: string, description?: string | null, runtime: string, syncStatus: ServerlessFunctionSyncStatus, latestVersion?: string | null, createdAt: any, updatedAt: any } };
+export type GetOneServerlessFunctionQuery = { __typename?: 'Query', findOneServerlessFunction: { __typename?: 'ServerlessFunction', id: any, name: string, description?: string | null, runtime: string, syncStatus: ServerlessFunctionSyncStatus, latestVersion?: string | null, latestVersionInputSchema?: any | null, publishedVersions: Array, createdAt: any, updatedAt: any } };
export type FindOneServerlessFunctionSourceCodeQueryVariables = Exact<{
input: GetServerlessFunctionSourceCodeInput;
@@ -1928,7 +2054,7 @@ export type FindOneServerlessFunctionSourceCodeQuery = { __typename?: 'Query', g
export const RemoteServerFieldsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"RemoteServerFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"RemoteServer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"foreignDataWrapperId"}},{"kind":"Field","name":{"kind":"Name","value":"foreignDataWrapperOptions"}},{"kind":"Field","name":{"kind":"Name","value":"foreignDataWrapperType"}},{"kind":"Field","name":{"kind":"Name","value":"userMappingOptions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user"}}]}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"schema"}},{"kind":"Field","name":{"kind":"Name","value":"label"}}]}}]} as unknown as DocumentNode;
export const RemoteTableFieldsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"RemoteTableFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"RemoteTable"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"schema"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"schemaPendingUpdates"}}]}}]} as unknown as DocumentNode;
-export const ServerlessFunctionFieldsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ServerlessFunctionFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ServerlessFunction"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"syncStatus"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersion"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode;
+export const ServerlessFunctionFieldsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ServerlessFunctionFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ServerlessFunction"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"syncStatus"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersion"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersionInputSchema"}},{"kind":"Field","name":{"kind":"Name","value":"publishedVersions"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode;
export const CreateServerDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"createServer"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"CreateRemoteServerInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createOneRemoteServer"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"RemoteServerFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"RemoteServerFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"RemoteServer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"foreignDataWrapperId"}},{"kind":"Field","name":{"kind":"Name","value":"foreignDataWrapperOptions"}},{"kind":"Field","name":{"kind":"Name","value":"foreignDataWrapperType"}},{"kind":"Field","name":{"kind":"Name","value":"userMappingOptions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user"}}]}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"schema"}},{"kind":"Field","name":{"kind":"Name","value":"label"}}]}}]} as unknown as DocumentNode;
export const DeleteServerDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"deleteServer"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"RemoteServerIdInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"deleteOneRemoteServer"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]} as unknown as DocumentNode;
export const SyncRemoteTableDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"syncRemoteTable"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"RemoteTableInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"syncRemoteTable"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"RemoteTableFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"RemoteTableFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"RemoteTable"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"schema"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"schemaPendingUpdates"}}]}}]} as unknown as DocumentNode;
@@ -1946,13 +2072,13 @@ export const UpdateOneObjectMetadataItemDocument = {"kind":"Document","definitio
export const DeleteOneObjectMetadataItemDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteOneObjectMetadataItem"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"idToDelete"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"deleteOneObject"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"idToDelete"}}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"dataSourceId"}},{"kind":"Field","name":{"kind":"Name","value":"nameSingular"}},{"kind":"Field","name":{"kind":"Name","value":"namePlural"}},{"kind":"Field","name":{"kind":"Name","value":"labelSingular"}},{"kind":"Field","name":{"kind":"Name","value":"labelPlural"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"isCustom"}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"labelIdentifierFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"imageIdentifierFieldMetadataId"}}]}}]}}]} as unknown as DocumentNode;
export const DeleteOneFieldMetadataItemDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteOneFieldMetadataItem"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"idToDelete"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"deleteOneField"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"idToDelete"}}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"label"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"isCustom"}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"isNullable"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"settings"}}]}}]}}]} as unknown as DocumentNode;
export const DeleteOneRelationMetadataItemDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteOneRelationMetadataItem"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"idToDelete"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"deleteOneRelation"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"idToDelete"}}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]} as unknown as DocumentNode;
-export const ObjectMetadataItemsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ObjectMetadataItems"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"objectFilter"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"objectFilter"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"fieldFilter"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"fieldFilter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"objects"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"paging"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"first"},"value":{"kind":"IntValue","value":"1000"}}]}},{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"objectFilter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"edges"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"node"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"dataSourceId"}},{"kind":"Field","name":{"kind":"Name","value":"nameSingular"}},{"kind":"Field","name":{"kind":"Name","value":"namePlural"}},{"kind":"Field","name":{"kind":"Name","value":"labelSingular"}},{"kind":"Field","name":{"kind":"Name","value":"labelPlural"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"isCustom"}},{"kind":"Field","name":{"kind":"Name","value":"isRemote"}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"isSystem"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"labelIdentifierFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"imageIdentifierFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"indexMetadatas"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"paging"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"first"},"value":{"kind":"IntValue","value":"100"}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"edges"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"node"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"indexWhereClause"}},{"kind":"Field","name":{"kind":"Name","value":"indexType"}},{"kind":"Field","name":{"kind":"Name","value":"isUnique"}},{"kind":"Field","name":{"kind":"Name","value":"indexFieldMetadatas"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"paging"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"first"},"value":{"kind":"IntValue","value":"100"}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"edges"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"node"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"order"}},{"kind":"Field","name":{"kind":"Name","value":"fieldMetadataId"}}]}}]}}]}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"fields"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"paging"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"first"},"value":{"kind":"IntValue","value":"1000"}}]}},{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"fieldFilter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"edges"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"node"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"label"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"isCustom"}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"isSystem"}},{"kind":"Field","name":{"kind":"Name","value":"isNullable"}},{"kind":"Field","name":{"kind":"Name","value":"isUnique"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"defaultValue"}},{"kind":"Field","name":{"kind":"Name","value":"options"}},{"kind":"Field","name":{"kind":"Name","value":"settings"}},{"kind":"Field","name":{"kind":"Name","value":"relationDefinition"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"relationId"}},{"kind":"Field","name":{"kind":"Name","value":"direction"}},{"kind":"Field","name":{"kind":"Name","value":"sourceObjectMetadata"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"nameSingular"}},{"kind":"Field","name":{"kind":"Name","value":"namePlural"}}]}},{"kind":"Field","name":{"kind":"Name","value":"sourceFieldMetadata"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"targetObjectMetadata"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"nameSingular"}},{"kind":"Field","name":{"kind":"Name","value":"namePlural"}}]}},{"kind":"Field","name":{"kind":"Name","value":"targetFieldMetadata"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"pageInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hasNextPage"}},{"kind":"Field","name":{"kind":"Name","value":"hasPreviousPage"}},{"kind":"Field","name":{"kind":"Name","value":"startCursor"}},{"kind":"Field","name":{"kind":"Name","value":"endCursor"}}]}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"pageInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hasNextPage"}},{"kind":"Field","name":{"kind":"Name","value":"hasPreviousPage"}},{"kind":"Field","name":{"kind":"Name","value":"startCursor"}},{"kind":"Field","name":{"kind":"Name","value":"endCursor"}}]}}]}}]}}]} as unknown as DocumentNode;
-export const CreateOneServerlessFunctionItemDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateOneServerlessFunctionItem"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"CreateServerlessFunctionInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createOneServerlessFunction"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ServerlessFunctionFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ServerlessFunctionFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ServerlessFunction"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"syncStatus"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersion"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode;
-export const DeleteOneServerlessFunctionDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteOneServerlessFunction"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"DeleteServerlessFunctionInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"deleteOneServerlessFunction"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ServerlessFunctionFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ServerlessFunctionFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ServerlessFunction"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"syncStatus"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersion"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode;
+export const ObjectMetadataItemsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ObjectMetadataItems"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"objectFilter"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"objectFilter"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"fieldFilter"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"fieldFilter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"objects"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"paging"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"first"},"value":{"kind":"IntValue","value":"1000"}}]}},{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"objectFilter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"edges"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"node"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"dataSourceId"}},{"kind":"Field","name":{"kind":"Name","value":"nameSingular"}},{"kind":"Field","name":{"kind":"Name","value":"namePlural"}},{"kind":"Field","name":{"kind":"Name","value":"labelSingular"}},{"kind":"Field","name":{"kind":"Name","value":"labelPlural"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"isCustom"}},{"kind":"Field","name":{"kind":"Name","value":"isRemote"}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"isSystem"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"labelIdentifierFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"imageIdentifierFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"shortcut"}},{"kind":"Field","name":{"kind":"Name","value":"isLabelSyncedWithName"}},{"kind":"Field","name":{"kind":"Name","value":"indexMetadatas"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"paging"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"first"},"value":{"kind":"IntValue","value":"100"}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"edges"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"node"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"indexWhereClause"}},{"kind":"Field","name":{"kind":"Name","value":"indexType"}},{"kind":"Field","name":{"kind":"Name","value":"isUnique"}},{"kind":"Field","name":{"kind":"Name","value":"indexFieldMetadatas"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"paging"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"first"},"value":{"kind":"IntValue","value":"100"}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"edges"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"node"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"order"}},{"kind":"Field","name":{"kind":"Name","value":"fieldMetadataId"}}]}}]}}]}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"fields"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"paging"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"first"},"value":{"kind":"IntValue","value":"1000"}}]}},{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"fieldFilter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"edges"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"node"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"label"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"isCustom"}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"isSystem"}},{"kind":"Field","name":{"kind":"Name","value":"isNullable"}},{"kind":"Field","name":{"kind":"Name","value":"isUnique"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"defaultValue"}},{"kind":"Field","name":{"kind":"Name","value":"options"}},{"kind":"Field","name":{"kind":"Name","value":"settings"}},{"kind":"Field","name":{"kind":"Name","value":"relationDefinition"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"relationId"}},{"kind":"Field","name":{"kind":"Name","value":"direction"}},{"kind":"Field","name":{"kind":"Name","value":"sourceObjectMetadata"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"nameSingular"}},{"kind":"Field","name":{"kind":"Name","value":"namePlural"}}]}},{"kind":"Field","name":{"kind":"Name","value":"sourceFieldMetadata"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"targetObjectMetadata"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"nameSingular"}},{"kind":"Field","name":{"kind":"Name","value":"namePlural"}}]}},{"kind":"Field","name":{"kind":"Name","value":"targetFieldMetadata"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"pageInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hasNextPage"}},{"kind":"Field","name":{"kind":"Name","value":"hasPreviousPage"}},{"kind":"Field","name":{"kind":"Name","value":"startCursor"}},{"kind":"Field","name":{"kind":"Name","value":"endCursor"}}]}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"pageInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hasNextPage"}},{"kind":"Field","name":{"kind":"Name","value":"hasPreviousPage"}},{"kind":"Field","name":{"kind":"Name","value":"startCursor"}},{"kind":"Field","name":{"kind":"Name","value":"endCursor"}}]}}]}}]}}]} as unknown as DocumentNode;
+export const CreateOneServerlessFunctionItemDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateOneServerlessFunctionItem"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"CreateServerlessFunctionInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createOneServerlessFunction"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ServerlessFunctionFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ServerlessFunctionFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ServerlessFunction"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"syncStatus"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersion"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersionInputSchema"}},{"kind":"Field","name":{"kind":"Name","value":"publishedVersions"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode;
+export const DeleteOneServerlessFunctionDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteOneServerlessFunction"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ServerlessFunctionIdInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"deleteOneServerlessFunction"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ServerlessFunctionFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ServerlessFunctionFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ServerlessFunction"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"syncStatus"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersion"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersionInputSchema"}},{"kind":"Field","name":{"kind":"Name","value":"publishedVersions"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode;
export const ExecuteOneServerlessFunctionDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"ExecuteOneServerlessFunction"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ExecuteServerlessFunctionInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"executeOneServerlessFunction"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"data"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"error"}}]}}]}}]} as unknown as DocumentNode;
-export const PublishOneServerlessFunctionDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"PublishOneServerlessFunction"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"PublishServerlessFunctionInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"publishServerlessFunction"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ServerlessFunctionFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ServerlessFunctionFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ServerlessFunction"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"syncStatus"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersion"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode;
-export const UpdateOneServerlessFunctionDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateOneServerlessFunction"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UpdateServerlessFunctionInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateOneServerlessFunction"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ServerlessFunctionFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ServerlessFunctionFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ServerlessFunction"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"syncStatus"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersion"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode;
+export const PublishOneServerlessFunctionDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"PublishOneServerlessFunction"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"PublishServerlessFunctionInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"publishServerlessFunction"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ServerlessFunctionFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ServerlessFunctionFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ServerlessFunction"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"syncStatus"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersion"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersionInputSchema"}},{"kind":"Field","name":{"kind":"Name","value":"publishedVersions"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode;
+export const UpdateOneServerlessFunctionDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateOneServerlessFunction"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UpdateServerlessFunctionInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateOneServerlessFunction"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ServerlessFunctionFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ServerlessFunctionFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ServerlessFunction"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"syncStatus"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersion"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersionInputSchema"}},{"kind":"Field","name":{"kind":"Name","value":"publishedVersions"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode;
export const FindManyAvailablePackagesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"FindManyAvailablePackages"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"getAvailablePackages"}}]}}]} as unknown as DocumentNode;
-export const GetManyServerlessFunctionsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetManyServerlessFunctions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"serverlessFunctions"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"paging"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"first"},"value":{"kind":"IntValue","value":"100"}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"edges"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"node"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ServerlessFunctionFields"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ServerlessFunctionFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ServerlessFunction"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"syncStatus"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersion"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode;
-export const GetOneServerlessFunctionDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetOneServerlessFunction"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"serverlessFunction"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ServerlessFunctionFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ServerlessFunctionFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ServerlessFunction"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"syncStatus"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersion"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode;
+export const GetManyServerlessFunctionsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetManyServerlessFunctions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"findManyServerlessFunctions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ServerlessFunctionFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ServerlessFunctionFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ServerlessFunction"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"syncStatus"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersion"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersionInputSchema"}},{"kind":"Field","name":{"kind":"Name","value":"publishedVersions"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode;
+export const GetOneServerlessFunctionDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetOneServerlessFunction"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ServerlessFunctionIdInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"findOneServerlessFunction"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ServerlessFunctionFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ServerlessFunctionFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ServerlessFunction"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"syncStatus"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersion"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersionInputSchema"}},{"kind":"Field","name":{"kind":"Name","value":"publishedVersions"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode;
export const FindOneServerlessFunctionSourceCodeDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"FindOneServerlessFunctionSourceCode"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"GetServerlessFunctionSourceCodeInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"getServerlessFunctionSourceCode"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}]}]}}]} as unknown as DocumentNode;
\ No newline at end of file
diff --git a/packages/twenty-front/src/generated/graphql.tsx b/packages/twenty-front/src/generated/graphql.tsx
index 9e930133b3fb..de35163609bc 100644
--- a/packages/twenty-front/src/generated/graphql.tsx
+++ b/packages/twenty-front/src/generated/graphql.tsx
@@ -1,5 +1,5 @@
-import * as Apollo from '@apollo/client';
import { gql } from '@apollo/client';
+import * as Apollo from '@apollo/client';
export type Maybe = T | null;
export type InputMaybe = Maybe;
export type Exact = { [K in keyof T]: T[K] };
@@ -21,13 +21,6 @@ export type Scalars = {
Upload: any;
};
-export type AisqlQueryResult = {
- __typename?: 'AISQLQueryResult';
- queryFailedErrorMessage?: Maybe;
- sqlQuery: Scalars['String'];
- sqlQueryResult?: Maybe;
-};
-
export type ActivateWorkspaceInput = {
displayName?: InputMaybe;
};
@@ -38,6 +31,16 @@ export type Analytics = {
success: Scalars['Boolean'];
};
+export type AnalyticsTinybirdJwtMap = {
+ __typename?: 'AnalyticsTinybirdJwtMap';
+ getPageviewsAnalytics: Scalars['String'];
+ getServerlessFunctionDuration: Scalars['String'];
+ getServerlessFunctionErrorCount: Scalars['String'];
+ getServerlessFunctionSuccessRate: Scalars['String'];
+ getUsersAnalytics: Scalars['String'];
+ getWebhookAnalytics: Scalars['String'];
+};
+
export type ApiConfig = {
__typename?: 'ApiConfig';
mutationMaximumAffectedRecords: Scalars['Float'];
@@ -71,6 +74,7 @@ export type AuthProviders = {
magicLink: Scalars['Boolean'];
microsoft: Scalars['Boolean'];
password: Scalars['Boolean'];
+ sso: Scalars['Boolean'];
};
export type AuthToken = {
@@ -148,6 +152,7 @@ export enum CaptchaDriverType {
export type ClientConfig = {
__typename?: 'ClientConfig';
+ analyticsEnabled: Scalars['Boolean'];
api: ApiConfig;
authProviders: AuthProviders;
billing: Billing;
@@ -160,13 +165,12 @@ export type ClientConfig = {
support: Support;
};
-export type CreateServerlessFunctionFromFileInput = {
- description?: InputMaybe;
- name: Scalars['String'];
+export type ComputeStepOutputSchemaInput = {
+ /** Step JSON format */
+ step: Scalars['JSON'];
};
export type CreateServerlessFunctionInput = {
- code: Scalars['String'];
description?: InputMaybe;
name: Scalars['String'];
};
@@ -187,9 +191,13 @@ export type DeleteOneObjectInput = {
id: Scalars['UUID'];
};
-export type DeleteServerlessFunctionInput = {
- /** The id of the function. */
- id: Scalars['ID'];
+export type DeleteSsoInput = {
+ identityProviderId: Scalars['String'];
+};
+
+export type DeleteSsoOutput = {
+ __typename?: 'DeleteSsoOutput';
+ identityProviderId: Scalars['String'];
};
/** Schema update on a table */
@@ -200,6 +208,20 @@ export enum DistantTableUpdate {
TableDeleted = 'TABLE_DELETED'
}
+export type EditSsoInput = {
+ id: Scalars['String'];
+ status: SsoIdentityProviderStatus;
+};
+
+export type EditSsoOutput = {
+ __typename?: 'EditSsoOutput';
+ id: Scalars['String'];
+ issuer: Scalars['String'];
+ name: Scalars['String'];
+ status: SsoIdentityProviderStatus;
+ type: IdpType;
+};
+
export type EmailPasswordResetLink = {
__typename?: 'EmailPasswordResetLink';
/** Boolean that confirms query was dispatched */
@@ -289,12 +311,53 @@ export enum FileFolder {
WorkspaceLogo = 'WorkspaceLogo'
}
+export type FindAvailableSsoidpInput = {
+ email: Scalars['String'];
+};
+
+export type FindAvailableSsoidpOutput = {
+ __typename?: 'FindAvailableSSOIDPOutput';
+ id: Scalars['String'];
+ issuer: Scalars['String'];
+ name: Scalars['String'];
+ status: SsoIdentityProviderStatus;
+ type: IdpType;
+ workspace: WorkspaceNameAndId;
+};
+
export type FullName = {
__typename?: 'FullName';
firstName: Scalars['String'];
lastName: Scalars['String'];
};
+export type GenerateJwt = GenerateJwtOutputWithAuthTokens | GenerateJwtOutputWithSsoauth;
+
+export type GenerateJwtOutputWithAuthTokens = {
+ __typename?: 'GenerateJWTOutputWithAuthTokens';
+ authTokens: AuthTokens;
+ reason: Scalars['String'];
+ success: Scalars['Boolean'];
+};
+
+export type GenerateJwtOutputWithSsoauth = {
+ __typename?: 'GenerateJWTOutputWithSSOAUTH';
+ availableSSOIDPs: Array;
+ reason: Scalars['String'];
+ success: Scalars['Boolean'];
+};
+
+export type GetAuthorizationUrlInput = {
+ identityProviderId: Scalars['String'];
+};
+
+export type GetAuthorizationUrlOutput = {
+ __typename?: 'GetAuthorizationUrlOutput';
+ authorizationURL: Scalars['String'];
+ id: Scalars['String'];
+ type: Scalars['String'];
+};
+
export type GetServerlessFunctionSourceCodeInput = {
/** The id of the function. */
id: Scalars['ID'];
@@ -302,6 +365,41 @@ export type GetServerlessFunctionSourceCodeInput = {
version?: Scalars['String'];
};
+export enum IdpType {
+ Oidc = 'OIDC',
+ Saml = 'SAML'
+}
+
+export type IndexConnection = {
+ __typename?: 'IndexConnection';
+ /** Array of edges. */
+ edges: Array;
+ /** Paging information */
+ pageInfo: PageInfo;
+};
+
+export type IndexIndexFieldMetadatasConnection = {
+ __typename?: 'IndexIndexFieldMetadatasConnection';
+ /** Array of edges. */
+ edges: Array;
+ /** Paging information */
+ pageInfo: PageInfo;
+};
+
+export type IndexObjectMetadataConnection = {
+ __typename?: 'IndexObjectMetadataConnection';
+ /** Array of edges. */
+ edges: Array;
+ /** Paging information */
+ pageInfo: PageInfo;
+};
+
+/** Type of the index */
+export enum IndexType {
+ Btree = 'BTREE',
+ Gin = 'GIN'
+}
+
export type InvalidatePassword = {
__typename?: 'InvalidatePassword';
/** Boolean that confirms query was dispatched */
@@ -341,24 +439,30 @@ export type Mutation = {
authorizeApp: AuthorizeApp;
challenge: LoginToken;
checkoutSession: SessionEntity;
+ computeStepOutputSchema: Scalars['JSON'];
+ createOIDCIdentityProvider: SetupSsoOutput;
createOneAppToken: AppToken;
createOneObject: Object;
createOneServerlessFunction: ServerlessFunction;
- createOneServerlessFunctionFromFile: ServerlessFunction;
+ createSAMLIdentityProvider: SetupSsoOutput;
deactivateWorkflowVersion: Scalars['Boolean'];
deleteCurrentWorkspace: Workspace;
deleteOneObject: Object;
deleteOneServerlessFunction: ServerlessFunction;
+ deleteSSOIdentityProvider: DeleteSsoOutput;
deleteUser: User;
deleteWorkspaceInvitation: Scalars['String'];
disablePostgresProxy: PostgresCredentials;
+ editSSOIdentityProvider: EditSsoOutput;
emailPasswordResetLink: EmailPasswordResetLink;
enablePostgresProxy: PostgresCredentials;
exchangeAuthorizationCode: ExchangeAuthCode;
executeOneServerlessFunction: ServerlessFunctionExecutionResult;
+ findAvailableSSOIdentityProviders: Array;
generateApiKeyToken: ApiKeyToken;
- generateJWT: AuthTokens;
+ generateJWT: GenerateJwt;
generateTransientToken: TransientToken;
+ getAuthorizationUrl: GetAuthorizationUrlOutput;
impersonate: Verify;
publishServerlessFunction: ServerlessFunction;
renewToken: AuthTokens;
@@ -421,14 +525,23 @@ export type MutationCheckoutSessionArgs = {
};
+export type MutationComputeStepOutputSchemaArgs = {
+ input: ComputeStepOutputSchemaInput;
+};
+
+
+export type MutationCreateOidcIdentityProviderArgs = {
+ input: SetupOidcSsoInput;
+};
+
+
export type MutationCreateOneServerlessFunctionArgs = {
input: CreateServerlessFunctionInput;
};
-export type MutationCreateOneServerlessFunctionFromFileArgs = {
- file: Scalars['Upload'];
- input: CreateServerlessFunctionFromFileInput;
+export type MutationCreateSamlIdentityProviderArgs = {
+ input: SetupSamlSsoInput;
};
@@ -443,7 +556,12 @@ export type MutationDeleteOneObjectArgs = {
export type MutationDeleteOneServerlessFunctionArgs = {
- input: DeleteServerlessFunctionInput;
+ input: ServerlessFunctionIdInput;
+};
+
+
+export type MutationDeleteSsoIdentityProviderArgs = {
+ input: DeleteSsoInput;
};
@@ -452,6 +570,11 @@ export type MutationDeleteWorkspaceInvitationArgs = {
};
+export type MutationEditSsoIdentityProviderArgs = {
+ input: EditSsoInput;
+};
+
+
export type MutationEmailPasswordResetLinkArgs = {
email: Scalars['String'];
};
@@ -469,6 +592,11 @@ export type MutationExecuteOneServerlessFunctionArgs = {
};
+export type MutationFindAvailableSsoIdentityProvidersArgs = {
+ input: FindAvailableSsoidpInput;
+};
+
+
export type MutationGenerateApiKeyTokenArgs = {
apiKeyId: Scalars['String'];
expiresAt: Scalars['String'];
@@ -480,6 +608,11 @@ export type MutationGenerateJwtArgs = {
};
+export type MutationGetAuthorizationUrlArgs = {
+ input: GetAuthorizationUrlInput;
+};
+
+
export type MutationImpersonateArgs = {
userId: Scalars['String'];
};
@@ -520,9 +653,8 @@ export type MutationSignUpArgs = {
export type MutationTrackArgs = {
- data: Scalars['JSON'];
- sessionId: Scalars['String'];
- type: Scalars['String'];
+ action: Scalars['String'];
+ payload: Scalars['JSON'];
};
@@ -589,6 +721,14 @@ export type ObjectFieldsConnection = {
pageInfo: PageInfo;
};
+export type ObjectIndexMetadatasConnection = {
+ __typename?: 'ObjectIndexMetadatasConnection';
+ /** Array of edges. */
+ edges: Array;
+ /** Paging information */
+ pageInfo: PageInfo;
+};
+
/** Onboarding status */
export enum OnboardingStatus {
Completed = 'COMPLETED',
@@ -652,21 +792,23 @@ export type Query = {
clientConfig: ClientConfig;
currentUser: User;
currentWorkspace: Workspace;
+ findManyServerlessFunctions: Array;
+ findOneServerlessFunction: ServerlessFunction;
findWorkspaceFromInviteHash: Workspace;
findWorkspaceInvitations: Array;
- getAISQLQuery: AisqlQueryResult;
getAvailablePackages: Scalars['JSON'];
getPostgresCredentials?: Maybe;
getProductPrices: ProductPricesEntity;
- getServerlessFunctionSourceCode?: Maybe;
+ getServerlessFunctionSourceCode?: Maybe;
getTimelineCalendarEventsFromCompanyId: TimelineCalendarEventsWithTotal;
getTimelineCalendarEventsFromPersonId: TimelineCalendarEventsWithTotal;
getTimelineThreadsFromCompanyId: TimelineThreadsWithTotal;
getTimelineThreadsFromPersonId: TimelineThreadsWithTotal;
+ index: Index;
+ indexMetadatas: IndexConnection;
+ listSSOIdentityProvidersByWorkspaceId: Array;
object: Object;
objects: ObjectConnection;
- serverlessFunction: ServerlessFunction;
- serverlessFunctions: ServerlessFunctionConnection;
validatePasswordResetToken: ValidatePasswordResetToken;
};
@@ -687,13 +829,13 @@ export type QueryCheckWorkspaceInviteHashIsValidArgs = {
};
-export type QueryFindWorkspaceFromInviteHashArgs = {
- inviteHash: Scalars['String'];
+export type QueryFindOneServerlessFunctionArgs = {
+ input: ServerlessFunctionIdInput;
};
-export type QueryGetAisqlQueryArgs = {
- text: Scalars['String'];
+export type QueryFindWorkspaceFromInviteHashArgs = {
+ inviteHash: Scalars['String'];
};
@@ -808,6 +950,12 @@ export type RunWorkflowVersionInput = {
workflowVersionId: Scalars['String'];
};
+export enum SsoIdentityProviderStatus {
+ Active = 'Active',
+ Error = 'Error',
+ Inactive = 'Inactive'
+}
+
export type SendInvitationsOutput = {
__typename?: 'SendInvitationsOutput';
errors: Array;
@@ -829,29 +977,14 @@ export type ServerlessFunction = {
description?: Maybe;
id: Scalars['UUID'];
latestVersion?: Maybe;
+ latestVersionInputSchema?: Maybe;
name: Scalars['String'];
+ publishedVersions: Array;
runtime: Scalars['String'];
- sourceCodeHash: Scalars['String'];
syncStatus: ServerlessFunctionSyncStatus;
updatedAt: Scalars['DateTime'];
};
-export type ServerlessFunctionConnection = {
- __typename?: 'ServerlessFunctionConnection';
- /** Array of edges. */
- edges: Array;
- /** Paging information */
- pageInfo: PageInfo;
-};
-
-export type ServerlessFunctionEdge = {
- __typename?: 'ServerlessFunctionEdge';
- /** Cursor for this node. */
- cursor: Scalars['ConnectionCursor'];
- /** The node containing the ServerlessFunction */
- node: ServerlessFunction;
-};
-
export type ServerlessFunctionExecutionResult = {
__typename?: 'ServerlessFunctionExecutionResult';
/** Execution result in JSON format */
@@ -870,6 +1003,11 @@ export enum ServerlessFunctionExecutionStatus {
Success = 'SUCCESS'
}
+export type ServerlessFunctionIdInput = {
+ /** The id of the function. */
+ id: Scalars['ID'];
+};
+
/** SyncStatus of the serverlessFunction */
export enum ServerlessFunctionSyncStatus {
NotReady = 'NOT_READY',
@@ -881,6 +1019,31 @@ export type SessionEntity = {
url?: Maybe;
};
+export type SetupOidcSsoInput = {
+ clientID: Scalars['String'];
+ clientSecret: Scalars['String'];
+ issuer: Scalars['String'];
+ name: Scalars['String'];
+};
+
+export type SetupSamlSsoInput = {
+ certificate: Scalars['String'];
+ fingerprint?: InputMaybe;
+ id: Scalars['String'];
+ issuer: Scalars['String'];
+ name: Scalars['String'];
+ ssoURL: Scalars['String'];
+};
+
+export type SetupSsoOutput = {
+ __typename?: 'SetupSsoOutput';
+ id: Scalars['String'];
+ issuer: Scalars['String'];
+ name: Scalars['String'];
+ status: SsoIdentityProviderStatus;
+ type: IdpType;
+};
+
/** Sort Directions */
export enum SortDirection {
Asc = 'ASC',
@@ -1014,11 +1177,13 @@ export type UpdateObjectPayload = {
icon?: InputMaybe;
imageIdentifierFieldMetadataId?: InputMaybe;
isActive?: InputMaybe;
+ isLabelSyncedWithName?: InputMaybe;
labelIdentifierFieldMetadataId?: InputMaybe;
labelPlural?: InputMaybe;
labelSingular?: InputMaybe;
namePlural?: InputMaybe;
nameSingular?: InputMaybe;
+ shortcut?: InputMaybe;
};
export type UpdateOneObjectInput = {
@@ -1028,7 +1193,7 @@ export type UpdateOneObjectInput = {
};
export type UpdateServerlessFunctionInput = {
- code: Scalars['String'];
+ code: Scalars['JSON'];
description?: InputMaybe;
/** Id of the serverless function to execute */
id: Scalars['UUID'];
@@ -1040,11 +1205,13 @@ export type UpdateWorkspaceInput = {
displayName?: InputMaybe;
domainName?: InputMaybe;
inviteHash?: InputMaybe;
+ isPublicInviteLinkEnabled?: InputMaybe;
logo?: InputMaybe;
};
export type User = {
__typename?: 'User';
+ analyticsTinybirdJwts?: Maybe;
canImpersonate: Scalars['Boolean'];
createdAt: Scalars['DateTime'];
defaultAvatarUrl?: Maybe;
@@ -1127,8 +1294,10 @@ export type Workspace = {
displayName?: Maybe;
domainName?: Maybe;
featureFlags?: Maybe>;
+ hasValidEntrepriseKey: Scalars['Boolean'];
id: Scalars['UUID'];
inviteHash?: Maybe;
+ isPublicInviteLinkEnabled: Scalars['Boolean'];
logo?: Maybe;
metadataVersion: Scalars['Float'];
updatedAt: Scalars['DateTime'];
@@ -1201,6 +1370,12 @@ export enum WorkspaceMemberTimeFormatEnum {
System = 'SYSTEM'
}
+export type WorkspaceNameAndId = {
+ __typename?: 'WorkspaceNameAndId';
+ displayName?: Maybe;
+ id: Scalars['String'];
+};
+
export type Field = {
__typename?: 'field';
createdAt: Scalars['DateTime'];
@@ -1213,6 +1388,7 @@ export type Field = {
isCustom?: Maybe;
isNullable?: Maybe;
isSystem?: Maybe;
+ isUnique?: Maybe;
label: Scalars['String'];
name: Scalars['String'];
object?: Maybe