App feedbacks fix option id required in apps (#19386)
fixes https://discord.com/channels/1130383047699738754/1488226371032453292
This commit is contained in:
@@ -51,28 +51,28 @@ export default defineObject({
|
||||
defaultValue: `'${PostCardStatus.DRAFT}'`,
|
||||
options: [
|
||||
{
|
||||
id: 'a1b2c3d4-0001-4000-8000-000000000001',
|
||||
id: '8ab3abad-02e7-4670-9283-983d7fac7fe4',
|
||||
value: PostCardStatus.DRAFT,
|
||||
label: 'Draft',
|
||||
position: 0,
|
||||
color: 'gray',
|
||||
},
|
||||
{
|
||||
id: 'a1b2c3d4-0002-4000-8000-000000000002',
|
||||
id: '2bcdd195-6c99-4d69-84b2-e2838ee54467',
|
||||
value: PostCardStatus.SENT,
|
||||
label: 'Sent',
|
||||
position: 1,
|
||||
color: 'orange',
|
||||
},
|
||||
{
|
||||
id: 'a1b2c3d4-0003-4000-8000-000000000003',
|
||||
id: '918ff60c-c26e-4fae-8eba-3fbce04dc48b',
|
||||
value: PostCardStatus.DELIVERED,
|
||||
label: 'Delivered',
|
||||
position: 2,
|
||||
color: 'green',
|
||||
},
|
||||
{
|
||||
id: 'a1b2c3d4-0004-4000-8000-000000000004',
|
||||
id: '3c91a653-5d31-4023-be6c-a69c68d21233',
|
||||
value: PostCardStatus.RETURNED,
|
||||
label: 'Returned',
|
||||
position: 3,
|
||||
|
||||
@@ -10,25 +10,31 @@ export default defineField({
|
||||
description: 'Post card category',
|
||||
options: [
|
||||
{
|
||||
id: 'c1d2e3f4-0001-4000-8000-000000000001',
|
||||
id: 'cd751c81-787d-4581-bc51-efe43f0050a7',
|
||||
value: 'PERSONAL',
|
||||
label: 'Personal',
|
||||
color: 'blue',
|
||||
position: 0,
|
||||
},
|
||||
{
|
||||
id: 'c1d2e3f4-0002-4000-8000-000000000002',
|
||||
id: 'eec437ca-5beb-41a9-a826-c9a5eca2eef4',
|
||||
value: 'BUSINESS',
|
||||
label: 'Business',
|
||||
color: 'green',
|
||||
position: 1,
|
||||
},
|
||||
{
|
||||
id: 'c1d2e3f4-0003-4000-8000-000000000003',
|
||||
id: 'a5baa37d-1047-4972-b6b8-7faae0e3eac1',
|
||||
value: 'PROMOTIONAL',
|
||||
label: 'Promotional',
|
||||
color: 'orange',
|
||||
position: 2,
|
||||
},
|
||||
{
|
||||
value: 'OTHER',
|
||||
label: 'Other',
|
||||
color: 'gray',
|
||||
position: 3,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
@@ -5,6 +5,7 @@ enum PostCardStatus {
|
||||
SENT = 'SENT',
|
||||
DELIVERED = 'DELIVERED',
|
||||
RETURNED = 'RETURNED',
|
||||
LOST = 'LOST',
|
||||
}
|
||||
|
||||
export const POST_CARD_UNIVERSAL_IDENTIFIER =
|
||||
@@ -53,33 +54,40 @@ export default defineObject({
|
||||
defaultValue: `'${PostCardStatus.DRAFT}'`,
|
||||
options: [
|
||||
{
|
||||
id: 'a1b2c3d4-0001-4000-8000-000000000001',
|
||||
id: '1b008e19-1e59-4a07-b187-65a20e547c4e',
|
||||
value: PostCardStatus.DRAFT,
|
||||
label: 'Draft',
|
||||
position: 0,
|
||||
color: 'gray',
|
||||
},
|
||||
{
|
||||
id: 'a1b2c3d4-0002-4000-8000-000000000002',
|
||||
id: '452b9d40-889c-4342-9697-98319394db04',
|
||||
value: PostCardStatus.SENT,
|
||||
label: 'Sent',
|
||||
position: 1,
|
||||
color: 'orange',
|
||||
},
|
||||
{
|
||||
id: 'a1b2c3d4-0003-4000-8000-000000000003',
|
||||
id: 'c2ed0b8c-a3ed-4383-aef9-e0441267bcfe',
|
||||
value: PostCardStatus.DELIVERED,
|
||||
label: 'Delivered',
|
||||
position: 2,
|
||||
color: 'green',
|
||||
},
|
||||
{
|
||||
id: 'a1b2c3d4-0004-4000-8000-000000000004',
|
||||
id: 'c57a5e08-7ef7-49b8-87e6-32d720d22802',
|
||||
value: PostCardStatus.RETURNED,
|
||||
label: 'Returned',
|
||||
position: 3,
|
||||
color: 'orange',
|
||||
},
|
||||
{
|
||||
// No id — exercises addMissingFieldOptionIds in the object branch
|
||||
value: PostCardStatus.LOST,
|
||||
label: 'Lost',
|
||||
position: 4,
|
||||
color: 'red',
|
||||
},
|
||||
],
|
||||
name: 'status',
|
||||
},
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request: {}
|
||||
|
||||
env:
|
||||
TWENTY_VERSION: latest
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Spawn Twenty instance
|
||||
id: twenty
|
||||
uses: twentyhq/twenty/.github/actions/spawn-twenty-docker-image@main
|
||||
with:
|
||||
twenty-version: ${{ env.TWENTY_VERSION }}
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Enable Corepack
|
||||
run: corepack enable
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'yarn'
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn install --immutable
|
||||
|
||||
- name: Run integration tests
|
||||
run: yarn test
|
||||
env:
|
||||
TWENTY_API_URL: ${{ steps.twenty.outputs.server-url }}
|
||||
TWENTY_API_KEY: ${{ steps.twenty.outputs.access-token }}
|
||||
@@ -1,38 +0,0 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.*
|
||||
.yarn
|
||||
|
||||
# codegen
|
||||
generated
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# dev
|
||||
/dist/
|
||||
|
||||
.twenty
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# env files (can opt-in for committing if needed)
|
||||
.env*
|
||||
|
||||
# typescript
|
||||
*.tsbuildinfo
|
||||
*.d.ts
|
||||
@@ -1 +0,0 @@
|
||||
24.5.0
|
||||
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"$schema": "./node_modules/oxlint/configuration_schema.json",
|
||||
"plugins": ["typescript"],
|
||||
"categories": {
|
||||
"correctness": "off"
|
||||
},
|
||||
"ignorePatterns": ["node_modules", "dist"],
|
||||
"rules": {
|
||||
"no-unused-vars": "off",
|
||||
|
||||
"typescript/no-unused-vars": [
|
||||
"warn",
|
||||
{
|
||||
"argsIgnorePattern": "^_"
|
||||
}
|
||||
],
|
||||
"typescript/no-explicit-any": "off"
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
nodeLinker: node-modules
|
||||
@@ -1,14 +0,0 @@
|
||||
## Base documentation
|
||||
|
||||
- Documentation: https://docs.twenty.com/developers/extend/apps/getting-started
|
||||
- Rich app example: https://github.com/twentyhq/twenty/tree/main/packages/twenty-apps/examples/postcard
|
||||
|
||||
## UUID requirement
|
||||
|
||||
- All generated UUIDs must be valid UUID v4.
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
- Creating an object without an index view associated. Unless this is a technical object, user will need to visualize it.
|
||||
- Creating a view without a navigationMenuItem associated. This will make the view available on the left sidebar.
|
||||
- Creating a front-end component that has a scroll instead of being responsive to its fixed widget height and width, unless it is specifically meant to be used in a canvas tab.
|
||||
@@ -1,14 +0,0 @@
|
||||
## Base documentation
|
||||
|
||||
- Documentation: https://docs.twenty.com/developers/extend/apps/getting-started
|
||||
- Rich app example: https://github.com/twentyhq/twenty/tree/main/packages/twenty-apps/examples/postcard
|
||||
|
||||
## UUID requirement
|
||||
|
||||
- All generated UUIDs must be valid UUID v4.
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
- Creating an object without an index view associated. Unless this is a technical object, user will need to visualize it.
|
||||
- Creating a view without a navigationMenuItem associated. This will make the view available on the left sidebar.
|
||||
- Creating a front-end component that has a scroll instead of being responsive to its fixed widget height and width, unless it is specifically meant to be used in a canvas tab.
|
||||
@@ -1,14 +0,0 @@
|
||||
## Base documentation
|
||||
|
||||
- Documentation: https://docs.twenty.com/developers/extend/apps/getting-started
|
||||
- Rich app example: https://github.com/twentyhq/twenty/tree/main/packages/twenty-apps/examples/postcard
|
||||
|
||||
## UUID requirement
|
||||
|
||||
- All generated UUIDs must be valid UUID v4.
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
- Creating an object without an index view associated. Unless this is a technical object, user will need to visualize it.
|
||||
- Creating a view without a navigationMenuItem associated. This will make the view available on the left sidebar.
|
||||
- Creating a front-end component that has a scroll instead of being responsive to its fixed widget height and width, unless it is specifically meant to be used in a canvas tab.
|
||||
@@ -1,11 +0,0 @@
|
||||
This is a [Twenty](https://twenty.com) application bootstrapped with [`create-twenty-app`](https://www.npmjs.com/package/create-twenty-app).
|
||||
|
||||
## Getting Started
|
||||
|
||||
Run `yarn twenty help` to list all available commands.
|
||||
|
||||
## Learn More
|
||||
|
||||
- [Twenty Apps documentation](https://docs.twenty.com/developers/extend/apps/getting-started)
|
||||
- [twenty-sdk CLI reference](https://www.npmjs.com/package/twenty-sdk)
|
||||
- [Discord](https://discord.gg/cx5n4Jzs57)
|
||||
@@ -1,35 +0,0 @@
|
||||
{
|
||||
"name": "my-twenty-app",
|
||||
"version": "0.1.0",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^24.5.0",
|
||||
"npm": "please-use-yarn",
|
||||
"yarn": ">=4.0.2"
|
||||
},
|
||||
"keywords": [
|
||||
"twenty-app"
|
||||
],
|
||||
"packageManager": "yarn@4.9.2",
|
||||
"scripts": {
|
||||
"twenty": "twenty",
|
||||
"lint": "oxlint -c .oxlintrc.json .",
|
||||
"lint:fix": "oxlint --fix -c .oxlintrc.json .",
|
||||
"test": "vitest run",
|
||||
"test:watch": "vitest"
|
||||
},
|
||||
"dependencies": {
|
||||
"twenty-client-sdk": "0.8.0-canary.8",
|
||||
"twenty-sdk": "0.8.0-canary.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^24.7.2",
|
||||
"@types/react": "^19.0.0",
|
||||
"oxlint": "^0.16.0",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"typescript": "^5.9.3",
|
||||
"vite-tsconfig-paths": "^4.2.1",
|
||||
"vitest": "^3.1.1"
|
||||
}
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
import { APPLICATION_UNIVERSAL_IDENTIFIER } from 'src/constants/universal-identifiers';
|
||||
import { appBuild, appDeploy, appInstall, appUninstall } from 'twenty-sdk/cli';
|
||||
import { MetadataApiClient } from 'twenty-client-sdk/metadata';
|
||||
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
|
||||
|
||||
const APP_PATH = process.cwd();
|
||||
|
||||
describe('App installation', () => {
|
||||
beforeAll(async () => {
|
||||
const buildResult = await appBuild({
|
||||
appPath: APP_PATH,
|
||||
tarball: true,
|
||||
onProgress: (message: string) => console.log(`[build] ${message}`),
|
||||
});
|
||||
|
||||
if (!buildResult.success) {
|
||||
throw new Error(
|
||||
`Build failed: ${buildResult.error?.message ?? 'Unknown error'}`,
|
||||
);
|
||||
}
|
||||
|
||||
const deployResult = await appDeploy({
|
||||
tarballPath: buildResult.data.tarballPath!,
|
||||
onProgress: (message: string) => console.log(`[deploy] ${message}`),
|
||||
});
|
||||
|
||||
if (!deployResult.success) {
|
||||
throw new Error(
|
||||
`Deploy failed: ${deployResult.error?.message ?? 'Unknown error'}`,
|
||||
);
|
||||
}
|
||||
|
||||
const installResult = await appInstall({ appPath: APP_PATH });
|
||||
|
||||
if (!installResult.success) {
|
||||
throw new Error(
|
||||
`Install failed: ${installResult.error?.message ?? 'Unknown error'}`,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
const uninstallResult = await appUninstall({ appPath: APP_PATH });
|
||||
|
||||
if (!uninstallResult.success) {
|
||||
console.warn(
|
||||
`App uninstall failed: ${uninstallResult.error?.message ?? 'Unknown error'}`,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it('should find the installed app in the applications list', async () => {
|
||||
const metadataClient = new MetadataApiClient();
|
||||
|
||||
const result = await metadataClient.query({
|
||||
findManyApplications: {
|
||||
id: true,
|
||||
name: true,
|
||||
universalIdentifier: true,
|
||||
},
|
||||
});
|
||||
|
||||
const installedApp = result.findManyApplications.find(
|
||||
(application: { universalIdentifier: string }) =>
|
||||
application.universalIdentifier === APPLICATION_UNIVERSAL_IDENTIFIER,
|
||||
);
|
||||
|
||||
expect(installedApp).toBeDefined();
|
||||
});
|
||||
});
|
||||
@@ -1,45 +0,0 @@
|
||||
import * as fs from 'fs';
|
||||
import * as os from 'os';
|
||||
import * as path from 'path';
|
||||
import { beforeAll } from 'vitest';
|
||||
|
||||
const TWENTY_API_URL = process.env.TWENTY_API_URL ?? 'http://localhost:2020';
|
||||
const TEST_CONFIG_DIR = path.join(os.tmpdir(), '.twenty-sdk-test');
|
||||
|
||||
const assertServerIsReachable = async () => {
|
||||
let response: Response;
|
||||
|
||||
try {
|
||||
response = await fetch(`${TWENTY_API_URL}/healthz`);
|
||||
} catch {
|
||||
throw new Error(
|
||||
`Twenty server is not reachable at ${TWENTY_API_URL}. ` +
|
||||
'Make sure the server is running before executing integration tests.',
|
||||
);
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Server at ${TWENTY_API_URL} returned ${response.status}`);
|
||||
}
|
||||
};
|
||||
|
||||
beforeAll(async () => {
|
||||
await assertServerIsReachable();
|
||||
|
||||
fs.mkdirSync(TEST_CONFIG_DIR, { recursive: true });
|
||||
|
||||
const configFile = {
|
||||
remotes: {
|
||||
local: {
|
||||
apiUrl: process.env.TWENTY_API_URL,
|
||||
apiKey: process.env.TWENTY_API_KEY,
|
||||
},
|
||||
},
|
||||
defaultRemote: 'local',
|
||||
};
|
||||
|
||||
fs.writeFileSync(
|
||||
path.join(TEST_CONFIG_DIR, 'config.json'),
|
||||
JSON.stringify(configFile, null, 2),
|
||||
);
|
||||
});
|
||||
@@ -1,15 +0,0 @@
|
||||
import { defineApplication } from 'twenty-sdk';
|
||||
|
||||
import {
|
||||
APP_DESCRIPTION,
|
||||
APP_DISPLAY_NAME,
|
||||
APPLICATION_UNIVERSAL_IDENTIFIER,
|
||||
DEFAULT_ROLE_UNIVERSAL_IDENTIFIER,
|
||||
} from 'src/constants/universal-identifiers';
|
||||
|
||||
export default defineApplication({
|
||||
universalIdentifier: APPLICATION_UNIVERSAL_IDENTIFIER,
|
||||
displayName: APP_DISPLAY_NAME,
|
||||
description: APP_DESCRIPTION,
|
||||
defaultRoleUniversalIdentifier: DEFAULT_ROLE_UNIVERSAL_IDENTIFIER,
|
||||
});
|
||||
@@ -1,4 +0,0 @@
|
||||
export const APP_DISPLAY_NAME = 'My twenty app';
|
||||
export const APP_DESCRIPTION = '';
|
||||
export const APPLICATION_UNIVERSAL_IDENTIFIER = '83a4b244-c80d-4923-b0d5-ae2406e83072';
|
||||
export const DEFAULT_ROLE_UNIVERSAL_IDENTIFIER = 'a1a28134-04d1-43ac-b8cc-b547e8cf97ba';
|
||||
@@ -1,16 +0,0 @@
|
||||
import { defineRole } from 'twenty-sdk';
|
||||
|
||||
import {
|
||||
APP_DISPLAY_NAME,
|
||||
DEFAULT_ROLE_UNIVERSAL_IDENTIFIER,
|
||||
} from 'src/constants/universal-identifiers';
|
||||
|
||||
export default defineRole({
|
||||
universalIdentifier: DEFAULT_ROLE_UNIVERSAL_IDENTIFIER,
|
||||
label: `${APP_DISPLAY_NAME} default function role`,
|
||||
description: `${APP_DISPLAY_NAME} default function role`,
|
||||
canReadAllObjectRecords: true,
|
||||
canUpdateAllObjectRecords: true,
|
||||
canSoftDeleteAllObjectRecords: true,
|
||||
canDestroyAllObjectRecords: false,
|
||||
});
|
||||
@@ -1,42 +0,0 @@
|
||||
{
|
||||
"compileOnSave": false,
|
||||
"compilerOptions": {
|
||||
"sourceMap": true,
|
||||
"declaration": true,
|
||||
"outDir": "./dist",
|
||||
"rootDir": ".",
|
||||
"jsx": "react-jsx",
|
||||
"moduleResolution": "node",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"importHelpers": true,
|
||||
"allowUnreachableCode": false,
|
||||
"strict": true,
|
||||
"alwaysStrict": true,
|
||||
"noImplicitAny": true,
|
||||
"strictBindCallApply": false,
|
||||
"target": "es2018",
|
||||
"module": "esnext",
|
||||
"lib": ["es2020", "dom"],
|
||||
"skipLibCheck": true,
|
||||
"skipDefaultLibCheck": true,
|
||||
"resolveJsonModule": true,
|
||||
"paths": {
|
||||
"src/*": ["./src/*"],
|
||||
"~/*": ["./*"]
|
||||
}
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"dist",
|
||||
"**/*.test.ts",
|
||||
"**/*.spec.ts",
|
||||
"**/*.integration-test.ts"
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.spec.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"types": ["vitest/globals", "node"]
|
||||
},
|
||||
"include": ["src/**/*.ts", "src/**/*.tsx"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
import tsconfigPaths from 'vite-tsconfig-paths';
|
||||
import { defineConfig } from 'vitest/config';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
tsconfigPaths({
|
||||
projects: ['tsconfig.spec.json'],
|
||||
ignoreConfigErrors: true,
|
||||
}),
|
||||
],
|
||||
test: {
|
||||
testTimeout: 120_000,
|
||||
hookTimeout: 120_000,
|
||||
include: ['src/**/*.integration-test.ts'],
|
||||
setupFiles: ['src/__tests__/setup-test.ts'],
|
||||
env: {
|
||||
TWENTY_API_KEY:
|
||||
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIyMDIwMjAyMC1lNmI1LTQ2ODAtOGEzMi1iODIwOTczNzE1NmIiLCJ1c2VySWQiOiIyMDIwMjAyMC1lNmI1LTQ2ODAtOGEzMi1iODIwOTczNzE1NmIiLCJ3b3Jrc3BhY2VJZCI6IjIwMjAyMDIwLTFjMjUtNGQwMi1iZjI1LTZhZWNjZjdlYTQxOSIsIndvcmtzcGFjZU1lbWJlcklkIjoiMjAyMDIwMjAtNDYzZi00MzViLTgyOGMtMTA3ZTAwN2EyNzExIiwidXNlcldvcmtzcGFjZUlkIjoiMjAyMDIwMjAtMWU3Yy00M2Q5LWE1ZGItNjg1YjUwNjlkODE2IiwidHlwZSI6IkFDQ0VTUyIsImF1dGhQcm92aWRlciI6InBhc3N3b3JkIiwiaWF0IjoxNzUxMjgxNzA0LCJleHAiOjIwNjY4NTc3MDR9.HMGqCsVlOAPVUBhKSGlD1X86VoHKt4LIUtET3CGIdik',
|
||||
},
|
||||
},
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1729,7 +1729,6 @@ enum FeatureFlagKey {
|
||||
IS_JSON_FILTER_ENABLED
|
||||
IS_AI_ENABLED
|
||||
IS_COMMAND_MENU_ITEM_ENABLED
|
||||
IS_MARKETPLACE_ENABLED
|
||||
IS_MARKETPLACE_SETTING_TAB_VISIBLE
|
||||
IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED
|
||||
IS_PUBLIC_DOMAIN_ENABLED
|
||||
|
||||
@@ -1429,7 +1429,7 @@ export interface PublicFeatureFlag {
|
||||
__typename: 'PublicFeatureFlag'
|
||||
}
|
||||
|
||||
export type FeatureFlagKey = 'IS_UNIQUE_INDEXES_ENABLED' | 'IS_JSON_FILTER_ENABLED' | 'IS_AI_ENABLED' | 'IS_COMMAND_MENU_ITEM_ENABLED' | 'IS_MARKETPLACE_ENABLED' | 'IS_MARKETPLACE_SETTING_TAB_VISIBLE' | 'IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED' | 'IS_PUBLIC_DOMAIN_ENABLED' | 'IS_EMAILING_DOMAIN_ENABLED' | 'IS_JUNCTION_RELATIONS_ENABLED' | 'IS_DRAFT_EMAIL_ENABLED' | 'IS_CONNECTED_ACCOUNT_MIGRATED' | 'IS_USAGE_ANALYTICS_ENABLED' | 'IS_RICH_TEXT_V1_MIGRATED' | 'IS_RECORD_PAGE_LAYOUT_GLOBAL_EDITION_ENABLED' | 'IS_RECORD_TABLE_WIDGET_ENABLED' | 'IS_DATASOURCE_MIGRATED'
|
||||
export type FeatureFlagKey = 'IS_UNIQUE_INDEXES_ENABLED' | 'IS_JSON_FILTER_ENABLED' | 'IS_AI_ENABLED' | 'IS_COMMAND_MENU_ITEM_ENABLED' | 'IS_MARKETPLACE_SETTING_TAB_VISIBLE' | 'IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED' | 'IS_PUBLIC_DOMAIN_ENABLED' | 'IS_EMAILING_DOMAIN_ENABLED' | 'IS_JUNCTION_RELATIONS_ENABLED' | 'IS_DRAFT_EMAIL_ENABLED' | 'IS_CONNECTED_ACCOUNT_MIGRATED' | 'IS_USAGE_ANALYTICS_ENABLED' | 'IS_RICH_TEXT_V1_MIGRATED' | 'IS_RECORD_PAGE_LAYOUT_GLOBAL_EDITION_ENABLED' | 'IS_RECORD_TABLE_WIDGET_ENABLED' | 'IS_DATASOURCE_MIGRATED'
|
||||
|
||||
export interface ClientConfigMaintenanceMode {
|
||||
startAt: Scalars['DateTime']
|
||||
@@ -9140,7 +9140,6 @@ export const enumFeatureFlagKey = {
|
||||
IS_JSON_FILTER_ENABLED: 'IS_JSON_FILTER_ENABLED' as const,
|
||||
IS_AI_ENABLED: 'IS_AI_ENABLED' as const,
|
||||
IS_COMMAND_MENU_ITEM_ENABLED: 'IS_COMMAND_MENU_ITEM_ENABLED' as const,
|
||||
IS_MARKETPLACE_ENABLED: 'IS_MARKETPLACE_ENABLED' as const,
|
||||
IS_MARKETPLACE_SETTING_TAB_VISIBLE: 'IS_MARKETPLACE_SETTING_TAB_VISIBLE' as const,
|
||||
IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED: 'IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED' as const,
|
||||
IS_PUBLIC_DOMAIN_ENABLED: 'IS_PUBLIC_DOMAIN_ENABLED' as const,
|
||||
|
||||
@@ -1736,7 +1736,6 @@ export enum FeatureFlagKey {
|
||||
IS_EMAILING_DOMAIN_ENABLED = 'IS_EMAILING_DOMAIN_ENABLED',
|
||||
IS_JSON_FILTER_ENABLED = 'IS_JSON_FILTER_ENABLED',
|
||||
IS_JUNCTION_RELATIONS_ENABLED = 'IS_JUNCTION_RELATIONS_ENABLED',
|
||||
IS_MARKETPLACE_ENABLED = 'IS_MARKETPLACE_ENABLED',
|
||||
IS_MARKETPLACE_SETTING_TAB_VISIBLE = 'IS_MARKETPLACE_SETTING_TAB_VISIBLE',
|
||||
IS_PUBLIC_DOMAIN_ENABLED = 'IS_PUBLIC_DOMAIN_ENABLED',
|
||||
IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED = 'IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED',
|
||||
|
||||
@@ -29,10 +29,6 @@ export const SettingsApplications = () => {
|
||||
PermissionFlagType.API_KEYS_AND_WEBHOOKS,
|
||||
);
|
||||
|
||||
const isMarketplaceEnabled = useIsFeatureEnabled(
|
||||
'IS_MARKETPLACE_ENABLED' as FeatureFlagKey,
|
||||
);
|
||||
|
||||
const isMarketplaceSettingTabVisible = useIsFeatureEnabled(
|
||||
FeatureFlagKey.IS_MARKETPLACE_SETTING_TAB_VISIBLE,
|
||||
);
|
||||
@@ -42,32 +38,6 @@ export const SettingsApplications = () => {
|
||||
APPLICATIONS_TAB_LIST_ID,
|
||||
);
|
||||
|
||||
const { data } = useQuery(FindManyApplicationsDocument);
|
||||
|
||||
const applications = data?.findManyApplications ?? [];
|
||||
|
||||
if (!isMarketplaceEnabled) {
|
||||
return (
|
||||
<SubMenuTopBarContainer
|
||||
title={t`Applications`}
|
||||
links={[
|
||||
{
|
||||
children: t`Workspace`,
|
||||
href: getSettingsPath(SettingsPath.Workspace),
|
||||
},
|
||||
{ children: t`Applications` },
|
||||
]}
|
||||
>
|
||||
<SettingsPageContainer>
|
||||
{applications.length > 0 && (
|
||||
<SettingsApplicationsTable applications={applications} />
|
||||
)}
|
||||
{hasDeveloperAccess && <SettingsApplicationsDeveloperTab />}
|
||||
</SettingsPageContainer>
|
||||
</SubMenuTopBarContainer>
|
||||
);
|
||||
}
|
||||
|
||||
const tabs = [
|
||||
...(isMarketplaceSettingTabVisible
|
||||
? [{ id: 'marketplace', title: t`Marketplace`, Icon: IconDownload }]
|
||||
|
||||
+2
@@ -2,6 +2,7 @@ import { runAppDevInProcess } from '@/cli/__tests__/integration/utils/run-app-de
|
||||
import { RICH_APP_PATH } from '@/cli/__tests__/apps/fixture-paths';
|
||||
|
||||
import { defineEntitiesTests } from './tests/entities.tests';
|
||||
import { defineFieldOptionIdsTests } from './tests/field-option-ids.tests';
|
||||
import { defineManifestTests } from './tests/manifest.tests';
|
||||
|
||||
describe('rich-app dev', () => {
|
||||
@@ -23,4 +24,5 @@ describe('rich-app dev', () => {
|
||||
|
||||
defineManifestTests(RICH_APP_PATH);
|
||||
defineEntitiesTests(RICH_APP_PATH);
|
||||
defineFieldOptionIdsTests(RICH_APP_PATH);
|
||||
});
|
||||
|
||||
+21
-7
@@ -251,25 +251,32 @@ export const EXPECTED_MANIFEST: Manifest = {
|
||||
options: [
|
||||
{
|
||||
color: 'blue',
|
||||
id: 'c1d2e3f4-0001-4000-8000-000000000001',
|
||||
id: 'cd751c81-787d-4581-bc51-efe43f0050a7',
|
||||
label: 'Personal',
|
||||
position: 0,
|
||||
value: 'PERSONAL',
|
||||
},
|
||||
{
|
||||
color: 'green',
|
||||
id: 'c1d2e3f4-0002-4000-8000-000000000002',
|
||||
id: 'eec437ca-5beb-41a9-a826-c9a5eca2eef4',
|
||||
label: 'Business',
|
||||
position: 1,
|
||||
value: 'BUSINESS',
|
||||
},
|
||||
{
|
||||
color: 'orange',
|
||||
id: 'c1d2e3f4-0003-4000-8000-000000000003',
|
||||
id: 'a5baa37d-1047-4972-b6b8-7faae0e3eac1',
|
||||
label: 'Promotional',
|
||||
position: 2,
|
||||
value: 'PROMOTIONAL',
|
||||
},
|
||||
{
|
||||
color: 'gray',
|
||||
id: '877336e4-6591-599f-8cd1-4c7dfae623d7',
|
||||
label: 'Other',
|
||||
position: 3,
|
||||
value: 'OTHER',
|
||||
},
|
||||
],
|
||||
type: FieldType.SELECT,
|
||||
universalIdentifier: 'b602dbd9-e511-49ce-b6d3-b697218dc69c',
|
||||
@@ -1043,32 +1050,39 @@ export const EXPECTED_MANIFEST: Manifest = {
|
||||
options: [
|
||||
{
|
||||
color: 'gray',
|
||||
id: 'a1b2c3d4-0001-4000-8000-000000000001',
|
||||
id: '1b008e19-1e59-4a07-b187-65a20e547c4e',
|
||||
label: 'Draft',
|
||||
position: 0,
|
||||
value: 'DRAFT',
|
||||
},
|
||||
{
|
||||
color: 'orange',
|
||||
id: 'a1b2c3d4-0002-4000-8000-000000000002',
|
||||
id: '452b9d40-889c-4342-9697-98319394db04',
|
||||
label: 'Sent',
|
||||
position: 1,
|
||||
value: 'SENT',
|
||||
},
|
||||
{
|
||||
color: 'green',
|
||||
id: 'a1b2c3d4-0003-4000-8000-000000000003',
|
||||
id: 'c2ed0b8c-a3ed-4383-aef9-e0441267bcfe',
|
||||
label: 'Delivered',
|
||||
position: 2,
|
||||
value: 'DELIVERED',
|
||||
},
|
||||
{
|
||||
color: 'orange',
|
||||
id: 'a1b2c3d4-0004-4000-8000-000000000004',
|
||||
id: 'c57a5e08-7ef7-49b8-87e6-32d720d22802',
|
||||
label: 'Returned',
|
||||
position: 3,
|
||||
value: 'RETURNED',
|
||||
},
|
||||
{
|
||||
color: 'red',
|
||||
id: '5248e3c4-23f8-512d-9bea-5c2df85bf83c',
|
||||
label: 'Lost',
|
||||
position: 4,
|
||||
value: 'LOST',
|
||||
},
|
||||
],
|
||||
type: FieldType.SELECT,
|
||||
universalIdentifier: '87b675b8-dd8c-4448-b4ca-20e5a2234a1e',
|
||||
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
import { join } from 'path';
|
||||
|
||||
import { readJson } from '@/cli/utilities/file/fs-utils';
|
||||
import {
|
||||
type FieldManifest,
|
||||
type Manifest,
|
||||
type ObjectFieldManifest,
|
||||
} from 'twenty-shared/application';
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
import { validate as isUuid } from 'uuid';
|
||||
|
||||
const POST_CARD_OBJECT_UNIVERSAL_IDENTIFIER =
|
||||
'54b589ca-eeed-4950-a176-358418b85c05';
|
||||
const POST_CARD_STATUS_FIELD_UNIVERSAL_IDENTIFIER =
|
||||
'87b675b8-dd8c-4448-b4ca-20e5a2234a1e';
|
||||
const POST_CARD_CATEGORY_FIELD_UNIVERSAL_IDENTIFIER =
|
||||
'b602dbd9-e511-49ce-b6d3-b697218dc69c';
|
||||
|
||||
export const defineFieldOptionIdsTests = (appPath: string): void => {
|
||||
const manifestOutputPath = join(appPath, '.twenty/output/manifest.json');
|
||||
|
||||
let manifest: Manifest;
|
||||
|
||||
beforeAll(async () => {
|
||||
manifest = await readJson<Manifest>(manifestOutputPath);
|
||||
});
|
||||
|
||||
describe('select option id injection (object inline branch)', () => {
|
||||
let statusField: ObjectFieldManifest | undefined;
|
||||
|
||||
beforeAll(() => {
|
||||
const postCard = manifest.objects.find(
|
||||
(object) =>
|
||||
object.universalIdentifier === POST_CARD_OBJECT_UNIVERSAL_IDENTIFIER,
|
||||
);
|
||||
statusField = postCard?.fields.find(
|
||||
(field) =>
|
||||
field.universalIdentifier ===
|
||||
POST_CARD_STATUS_FIELD_UNIVERSAL_IDENTIFIER,
|
||||
);
|
||||
});
|
||||
|
||||
it('should find the inline status SELECT field with all 5 options', () => {
|
||||
expect(statusField).toBeDefined();
|
||||
expect(statusField?.type).toBe(FieldMetadataType.SELECT);
|
||||
expect(statusField?.options).toHaveLength(5);
|
||||
});
|
||||
|
||||
it('should give every option a defined, valid UUID id', () => {
|
||||
for (const option of statusField?.options ?? []) {
|
||||
expect(option.id).toBeDefined();
|
||||
expect(typeof option.id).toBe('string');
|
||||
expect(isUuid(option.id)).toBe(true);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('select option id injection (standalone field branch)', () => {
|
||||
let categoryField: FieldManifest | undefined;
|
||||
|
||||
beforeAll(() => {
|
||||
categoryField = manifest.fields.find(
|
||||
(field) =>
|
||||
field.universalIdentifier ===
|
||||
POST_CARD_CATEGORY_FIELD_UNIVERSAL_IDENTIFIER,
|
||||
);
|
||||
});
|
||||
|
||||
it('should find the standalone category SELECT field with all 4 options', () => {
|
||||
expect(categoryField).toBeDefined();
|
||||
expect(categoryField?.type).toBe(FieldMetadataType.SELECT);
|
||||
expect(categoryField?.options).toHaveLength(4);
|
||||
});
|
||||
|
||||
it('should give every option a defined, valid UUID id', () => {
|
||||
for (const option of categoryField?.options ?? []) {
|
||||
expect(option.id).toBeDefined();
|
||||
expect(typeof option.id).toBe('string');
|
||||
expect(isUuid(option.id)).toBe(true);
|
||||
}
|
||||
});
|
||||
|
||||
it('should inject a fresh UUID for the OTHER option that has no source id', () => {
|
||||
const otherOption = categoryField?.options?.find(
|
||||
(option) => option.value === 'OTHER',
|
||||
);
|
||||
|
||||
expect(otherOption).toBeDefined();
|
||||
expect(otherOption?.id).toBeDefined();
|
||||
expect(isUuid(otherOption?.id ?? '')).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('select option id injection (manifest-wide invariant)', () => {
|
||||
it('should ensure every SELECT/MULTI_SELECT option in the manifest has a defined id', () => {
|
||||
const collectSelectOptions = (
|
||||
field: ObjectFieldManifest | FieldManifest,
|
||||
) => {
|
||||
if (
|
||||
field.type !== FieldMetadataType.SELECT &&
|
||||
field.type !== FieldMetadataType.MULTI_SELECT
|
||||
) {
|
||||
return [];
|
||||
}
|
||||
return field.options ?? [];
|
||||
};
|
||||
|
||||
const allOptions = [
|
||||
...manifest.objects.flatMap((object) =>
|
||||
object.fields.flatMap(collectSelectOptions),
|
||||
),
|
||||
...manifest.fields.flatMap(collectSelectOptions),
|
||||
];
|
||||
|
||||
expect(allOptions.length).toBeGreaterThan(0);
|
||||
|
||||
for (const option of allOptions) {
|
||||
expect(option.id).toBeDefined();
|
||||
expect(isUuid(option.id)).toBe(true);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
@@ -37,6 +37,7 @@ import {
|
||||
} from 'twenty-shared/application';
|
||||
import { getInputSchemaFromSourceCode } from 'twenty-shared/logic-function';
|
||||
import { assertUnreachable } from 'twenty-shared/utils';
|
||||
import { addMissingFieldOptionIds } from '@/cli/utilities/build/manifest/utils/add-missing-field-option-ids';
|
||||
|
||||
const loadSources = async (appPath: string): Promise<string[]> => {
|
||||
return await glob(['**/*.ts', '**/*.tsx'], {
|
||||
@@ -145,7 +146,7 @@ export const buildManifest = async (
|
||||
|
||||
const objectManifest: ObjectManifest = {
|
||||
...extract.config,
|
||||
fields: objectFieldsWithDefaults,
|
||||
fields: objectFieldsWithDefaults.map(addMissingFieldOptionIds),
|
||||
labelIdentifierFieldMetadataUniversalIdentifier,
|
||||
};
|
||||
|
||||
@@ -161,7 +162,8 @@ export const buildManifest = async (
|
||||
appPath,
|
||||
filePath,
|
||||
});
|
||||
fields.push(extract.config);
|
||||
const fieldConfig = addMissingFieldOptionIds(extract.config);
|
||||
fields.push(fieldConfig);
|
||||
errors.push(...extract.errors);
|
||||
fieldsFilePaths.push(relativePath);
|
||||
break;
|
||||
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
import { addMissingFieldOptionIds } from '@/cli/utilities/build/manifest/utils/add-missing-field-option-ids';
|
||||
import { type FieldManifest } from 'twenty-shared/application';
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
import { validate } from 'uuid';
|
||||
|
||||
const buildSelectField = (
|
||||
options: FieldManifest<FieldMetadataType.SELECT>['options'],
|
||||
): FieldManifest<FieldMetadataType.SELECT> => ({
|
||||
universalIdentifier: 'aaaaaaaa-aaaa-4aaa-aaaa-aaaaaaaaaaaa',
|
||||
objectUniversalIdentifier: 'bbbbbbbb-bbbb-4bbb-bbbb-bbbbbbbbbbbb',
|
||||
name: 'status',
|
||||
label: 'Status',
|
||||
type: FieldMetadataType.SELECT,
|
||||
options,
|
||||
});
|
||||
|
||||
describe('addMissingFieldOptionIds', () => {
|
||||
it('should add a UUID v4 id to every option that is missing one for a SELECT field', () => {
|
||||
const fieldManifest = buildSelectField([
|
||||
{ color: 'green', label: 'Open', value: 'OPEN', position: 1 },
|
||||
{ color: 'red', label: 'Closed', value: 'CLOSED', position: 2 },
|
||||
]);
|
||||
|
||||
const result = addMissingFieldOptionIds(fieldManifest);
|
||||
|
||||
expect(result.options).toHaveLength(2);
|
||||
for (const option of result.options ?? []) {
|
||||
expect(validate(option.id)).toBe(true);
|
||||
}
|
||||
});
|
||||
|
||||
it('should preserve existing ids on options that already have one', () => {
|
||||
const existingId = 'cccccccc-cccc-4ccc-cccc-cccccccccccc';
|
||||
const fieldManifest = buildSelectField([
|
||||
{
|
||||
id: existingId,
|
||||
color: 'green',
|
||||
label: 'Open',
|
||||
value: 'OPEN',
|
||||
position: 1,
|
||||
},
|
||||
{ color: 'red', label: 'Closed', value: 'CLOSED', position: 2 },
|
||||
]);
|
||||
|
||||
const result = addMissingFieldOptionIds(fieldManifest);
|
||||
|
||||
expect(result.options?.[0].id).toBe(existingId);
|
||||
expect(validate(result.options?.[1].id)).toBe(true);
|
||||
expect(result.options?.[1].id).not.toBe(existingId);
|
||||
});
|
||||
|
||||
it('should also process MULTI_SELECT fields', () => {
|
||||
const fieldManifest: FieldManifest<FieldMetadataType.MULTI_SELECT> = {
|
||||
universalIdentifier: 'dddddddd-dddd-4ddd-dddd-dddddddddddd',
|
||||
objectUniversalIdentifier: 'eeeeeeee-eeee-4eee-eeee-eeeeeeeeeeee',
|
||||
name: 'tags',
|
||||
label: 'Tags',
|
||||
type: FieldMetadataType.MULTI_SELECT,
|
||||
options: [
|
||||
{ color: 'blue', label: 'Urgent', value: 'URGENT', position: 1 },
|
||||
],
|
||||
};
|
||||
|
||||
const result = addMissingFieldOptionIds(fieldManifest);
|
||||
|
||||
expect(validate(result.options?.[0].id)).toBe(true);
|
||||
});
|
||||
|
||||
it('should return the field manifest unchanged when type is not SELECT or MULTI_SELECT', () => {
|
||||
const fieldManifest: FieldManifest<FieldMetadataType.TEXT> = {
|
||||
universalIdentifier: 'ffffffff-ffff-4fff-ffff-ffffffffffff',
|
||||
objectUniversalIdentifier: '11111111-1111-4111-1111-111111111111',
|
||||
name: 'description',
|
||||
label: 'Description',
|
||||
type: FieldMetadataType.TEXT,
|
||||
};
|
||||
|
||||
const result = addMissingFieldOptionIds(fieldManifest);
|
||||
|
||||
expect(result).toBe(fieldManifest);
|
||||
});
|
||||
|
||||
it('should return the field manifest unchanged when options is undefined', () => {
|
||||
const fieldManifest = buildSelectField(undefined);
|
||||
|
||||
const result = addMissingFieldOptionIds(fieldManifest);
|
||||
|
||||
expect(result).toBe(fieldManifest);
|
||||
});
|
||||
|
||||
it('should not mutate the original field manifest or its options', () => {
|
||||
const originalOptions = [
|
||||
{ color: 'green' as const, label: 'Open', value: 'OPEN', position: 1 },
|
||||
{ color: 'red' as const, label: 'Closed', value: 'CLOSED', position: 2 },
|
||||
];
|
||||
const fieldManifest = buildSelectField([...originalOptions]);
|
||||
|
||||
addMissingFieldOptionIds(fieldManifest);
|
||||
|
||||
expect(fieldManifest.options).toEqual(originalOptions);
|
||||
for (const option of fieldManifest.options ?? []) {
|
||||
expect(option.id).toBeUndefined();
|
||||
}
|
||||
});
|
||||
|
||||
it('should generate distinct ids for different missing options', () => {
|
||||
const fieldManifest = buildSelectField([
|
||||
{ color: 'green', label: 'A', value: 'A', position: 1 },
|
||||
{ color: 'red', label: 'B', value: 'B', position: 2 },
|
||||
{ color: 'blue', label: 'C', value: 'C', position: 3 },
|
||||
]);
|
||||
|
||||
const result = addMissingFieldOptionIds(fieldManifest);
|
||||
|
||||
const ids = result.options?.map((option) => option.id) ?? [];
|
||||
expect(new Set(ids).size).toBe(ids.length);
|
||||
});
|
||||
});
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
import type {
|
||||
FieldManifest,
|
||||
ObjectFieldManifest,
|
||||
} from 'twenty-shared/application';
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
import { v5 } from 'uuid';
|
||||
|
||||
const OPTION_ID_NAMESPACE = 'a80ff791-b940-4c47-a522-2bb478515415';
|
||||
|
||||
export const addMissingFieldOptionIds = <
|
||||
T extends FieldManifest | ObjectFieldManifest,
|
||||
>(
|
||||
fieldManifest: T,
|
||||
): T => {
|
||||
if (
|
||||
fieldManifest.type !== FieldMetadataType.SELECT &&
|
||||
fieldManifest.type !== FieldMetadataType.MULTI_SELECT
|
||||
) {
|
||||
return fieldManifest;
|
||||
}
|
||||
|
||||
if (fieldManifest.options === undefined || fieldManifest.options === null) {
|
||||
return fieldManifest;
|
||||
}
|
||||
|
||||
return {
|
||||
...fieldManifest,
|
||||
options: fieldManifest.options.map((option) => ({
|
||||
...option,
|
||||
id:
|
||||
option.id ??
|
||||
v5(
|
||||
`${option.label}-${fieldManifest.universalIdentifier}`,
|
||||
OPTION_ID_NAMESPACE,
|
||||
),
|
||||
})),
|
||||
};
|
||||
};
|
||||
+1
-1
@@ -21,7 +21,7 @@ export class OAuthDiscoveryController {
|
||||
async getAuthorizationServerMetadata() {
|
||||
const serverUrl = this.twentyConfigService.get('SERVER_URL');
|
||||
|
||||
const frontUrl = this.domainServerConfigService.getFrontUrl().toString();
|
||||
const frontUrl = this.domainServerConfigService.getBaseUrl().toString();
|
||||
|
||||
const cliRegistration =
|
||||
await this.applicationRegistrationService.findOneByUniversalIdentifier(
|
||||
|
||||
-1
@@ -233,7 +233,6 @@ describe('WorkspaceEntityManager', () => {
|
||||
IS_UNIQUE_INDEXES_ENABLED: false,
|
||||
IS_JSON_FILTER_ENABLED: false,
|
||||
IS_AI_ENABLED: false,
|
||||
IS_MARKETPLACE_ENABLED: false,
|
||||
IS_MARKETPLACE_SETTING_TAB_VISIBLE: false,
|
||||
IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED: false,
|
||||
IS_PUBLIC_DOMAIN_ENABLED: false,
|
||||
|
||||
-5
@@ -45,11 +45,6 @@ export const seedFeatureFlags = async ({
|
||||
workspaceId: workspaceId,
|
||||
value: true,
|
||||
},
|
||||
{
|
||||
key: FeatureFlagKey.IS_MARKETPLACE_ENABLED,
|
||||
workspaceId: workspaceId,
|
||||
value: true,
|
||||
},
|
||||
{
|
||||
key: FeatureFlagKey.IS_MARKETPLACE_SETTING_TAB_VISIBLE,
|
||||
workspaceId: workspaceId,
|
||||
|
||||
@@ -3,7 +3,6 @@ export enum FeatureFlagKey {
|
||||
IS_JSON_FILTER_ENABLED = 'IS_JSON_FILTER_ENABLED',
|
||||
IS_AI_ENABLED = 'IS_AI_ENABLED',
|
||||
IS_COMMAND_MENU_ITEM_ENABLED = 'IS_COMMAND_MENU_ITEM_ENABLED',
|
||||
IS_MARKETPLACE_ENABLED = 'IS_MARKETPLACE_ENABLED',
|
||||
IS_MARKETPLACE_SETTING_TAB_VISIBLE = 'IS_MARKETPLACE_SETTING_TAB_VISIBLE',
|
||||
IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED = 'IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED',
|
||||
IS_PUBLIC_DOMAIN_ENABLED = 'IS_PUBLIC_DOMAIN_ENABLED',
|
||||
|
||||
Reference in New Issue
Block a user