-
Notifications
You must be signed in to change notification settings - Fork 0
[WIP] feat: add meeting-baas-recorder Twenty app #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
3a015d7
5af0c3a
e4e0640
aa511a6
9800390
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| # Meeting BaaS API key (get yours at https://meetingbaas.com) | ||
| MEETING_BAAS_API_KEY= | ||
|
|
||
| # Twenty CRM API key | ||
| TWENTY_API_KEY= | ||
|
|
||
| # Twenty server URL (default: http://localhost:3000) | ||
| SERVER_URL=http://localhost:3000 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| .yarn/install-state.gz | ||
| .env |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| import { defineApplication } from 'twenty-sdk'; | ||
| import { DEFAULT_ROLE_ID } from './src/roles/default.role'; | ||
|
|
||
| export default defineApplication({ | ||
| universalIdentifier: 'c522c3c7-cff8-5c08-8c87-d1481adbd4a9', | ||
| displayName: 'Meeting BaaS Recorder', | ||
| description: 'Record meetings via Meeting BaaS and sync recordings, transcripts, and participants into Twenty.', | ||
| icon: 'IconVideo', | ||
| defaultRoleUniversalIdentifier: DEFAULT_ROLE_ID, | ||
| settingsCustomTabFrontComponentUniversalIdentifier: | ||
| '7f2c17b4-2cd2-5447-b7d1-83ef12040837', | ||
| applicationVariables: { | ||
| MEETING_BAAS_API_KEY: { | ||
| universalIdentifier: '32cd6297-bbd3-5beb-a0f6-1f5662590f66', | ||
| description: 'Meeting BaaS API key for authenticating requests and verifying webhooks', | ||
| isSecret: true, | ||
| value: '', | ||
| }, | ||
| }, | ||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| const jestConfig = { | ||
| displayName: 'meeting-baas-recorder', | ||
| preset: 'ts-jest', | ||
| testEnvironment: 'node', | ||
| moduleFileExtensions: ['ts', 'js'], | ||
| transform: { | ||
| '^.+\\.ts$': 'ts-jest', | ||
| }, | ||
| testMatch: [ | ||
| '<rootDir>/src/**/__tests__/**/*.(test|spec).{js,ts}', | ||
| '<rootDir>/src/**/?(*.)(test|spec).{js,ts}', | ||
| ], | ||
| setupFilesAfterEnv: [ | ||
| '<rootDir>/src/__tests__/setup.ts' | ||
| ], | ||
| collectCoverageFrom: [ | ||
| 'src/**/*.{ts,js}', | ||
| '!src/**/*.d.ts', | ||
| ], | ||
| coverageDirectory: './coverage', | ||
| }; | ||
|
|
||
| export default jestConfig; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| { | ||
| "name": "meeting-baas-recorder", | ||
| "version": "0.2.0", | ||
| "license": "MIT", | ||
| "engines": { | ||
| "node": "^24.5.0", | ||
| "npm": "please-use-yarn", | ||
| "yarn": ">=4.0.2" | ||
| }, | ||
|
Comment on lines
+5
to
+9
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Overly strict Node engine range may break installs in the monorepo.
🤖 Prompt for AI Agents |
||
| "packageManager": "yarn@4.9.2", | ||
| "scripts": { | ||
| "test": "jest" | ||
| }, | ||
| "dependencies": { | ||
| "@meeting-baas/sdk": "^6.0.5", | ||
| "axios": "^1.13.1", | ||
| "twenty-sdk": "0.8.0" | ||
| }, | ||
| "devDependencies": { | ||
| "@emotion/react": "^11.14.0", | ||
| "@emotion/styled": "^11.14.0", | ||
| "@types/jest": "^29.5.5", | ||
| "@types/node": "^24.9.2", | ||
| "@types/react": "^18.2.0", | ||
| "jest": "^29.7.0", | ||
| "react": "^18.2.0", | ||
| "ts-jest": "^29.1.1", | ||
| "typescript": "^5.9.3" | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| { | ||
| "name": "meeting-baas-recorder", | ||
| "$schema": "../../../../node_modules/nx/schemas/project-schema.json", | ||
| "sourceRoot": "packages/twenty-apps/community/meeting-baas-recorder/src", | ||
| "projectType": "application", | ||
| "tags": [ | ||
| "scope:apps" | ||
| ], | ||
| "targets": { | ||
| "test": { | ||
| "executor": "@nx/jest:jest", | ||
| "outputs": [ | ||
| "{workspaceRoot}/coverage/{projectRoot}" | ||
| ], | ||
| "options": { | ||
| "jestConfig": "packages/twenty-apps/community/meeting-baas-recorder/jest.config.mjs", | ||
| "passWithNoTests": true | ||
| }, | ||
| "configurations": { | ||
| "ci": { | ||
| "ci": true, | ||
| "coverageReporters": ["text"] | ||
| } | ||
| } | ||
| }, | ||
| "typecheck": { | ||
| "dependsOn": ["^build"] | ||
| }, | ||
| "lint": { | ||
| "executor": "@nx/eslint:lint", | ||
| "outputs": [ | ||
| "{options.outputFile}" | ||
| ], | ||
| "options": { | ||
| "lintFilePatterns": [ | ||
| "packages/twenty-apps/community/meeting-baas-recorder/**/*.{ts,tsx,js,jsx}" | ||
| ] | ||
| }, | ||
| "configurations": { | ||
| "fix": { | ||
| "fix": true | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,76 @@ | ||
| import { APPLICATION_UNIVERSAL_IDENTIFIER } from '../constants/universal-identifiers'; | ||
| import { | ||
| ensureIntegrationTestEnvironment, | ||
| hasIntegrationTestEnvironment, | ||
| } from './setup-test'; | ||
| 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.skipIf(!hasIntegrationTestEnvironment())('App installation', () => { | ||
| beforeAll(async () => { | ||
| await ensureIntegrationTestEnvironment(); | ||
|
|
||
| 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!, | ||
|
Comment on lines
+28
to
+29
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick | 🔵 Trivial Non-null assertion on
🤖 Prompt for AI Agents |
||
| 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'}`, | ||
| ); | ||
| } | ||
| }); | ||
|
Comment on lines
+48
to
+56
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Teardown may run uninstall when install never succeeded. If 🤖 Prompt for AI Agents |
||
|
|
||
| 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(); | ||
| }); | ||
| }); | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,55 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import * as fs from 'fs'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import * as os from 'os'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import * as path from 'path'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const CONFIG_DIR = path.join(os.homedir(), '.twenty'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const CONFIG_PATH = path.join(CONFIG_DIR, 'config.test.json'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export const hasIntegrationTestEnvironment = (): boolean => | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Boolean(process.env.TWENTY_API_URL && process.env.TWENTY_API_KEY); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export const ensureIntegrationTestEnvironment = async () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const apiUrl = process.env.TWENTY_API_URL!; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const token = process.env.TWENTY_API_KEY!; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!apiUrl || !token) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| throw new Error( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'TWENTY_API_URL and TWENTY_API_KEY must be set.\n' + | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'Start a local server: yarn twenty server start\n' + | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'Or set them in vitest env config.', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+12
to
+21
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick | 🔵 Trivial Remove misleading non-null assertions.
Proposed fix- const apiUrl = process.env.TWENTY_API_URL!;
- const token = process.env.TWENTY_API_KEY!;
-
- if (!apiUrl || !token) {
+ const apiUrl = process.env.TWENTY_API_URL;
+ const token = process.env.TWENTY_API_KEY;
+
+ if (!apiUrl || !token) {
throw new Error(
'TWENTY_API_URL and TWENTY_API_KEY must be set.\n' +
'Start a local server: yarn twenty server start\n' +
'Or set them in vitest env config.',
);
}🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let response: Response; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| response = await fetch(`${apiUrl}/healthz`); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| throw new Error( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| `Twenty server is not reachable at ${apiUrl}. ` + | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'Make sure the server is running before executing integration tests.', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!response.ok) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| throw new Error(`Server at ${apiUrl} returned ${response.status}`); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fs.mkdirSync(CONFIG_DIR, { recursive: true }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fs.writeFileSync( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| CONFIG_PATH, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| JSON.stringify( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| remotes: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| local: { apiUrl, apiKey: token }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| defaultRemote: 'local', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| null, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 2, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+38
to
+52
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Restrict permissions on the written config file.
Proposed fix- fs.mkdirSync(CONFIG_DIR, { recursive: true });
-
- fs.writeFileSync(
+ fs.mkdirSync(CONFIG_DIR, { recursive: true, mode: 0o700 });
+
+ fs.writeFileSync(
CONFIG_PATH,
JSON.stringify(
{
remotes: {
local: { apiUrl, apiKey: token },
},
defaultRemote: 'local',
},
null,
2,
),
+ { mode: 0o600 },
);📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| process.env.TWENTY_APP_ACCESS_TOKEN ??= token; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| import { defineApplication } from 'twenty-sdk'; | ||
|
|
||
| import { | ||
| APP_DESCRIPTION, | ||
| APP_DISPLAY_NAME, | ||
| APPLICATION_UNIVERSAL_IDENTIFIER, | ||
| DEFAULT_ROLE_UNIVERSAL_IDENTIFIER, | ||
| } from './constants/universal-identifiers'; | ||
|
|
||
| export default defineApplication({ | ||
| universalIdentifier: APPLICATION_UNIVERSAL_IDENTIFIER, | ||
| displayName: APP_DISPLAY_NAME, | ||
| description: APP_DESCRIPTION, | ||
| icon: 'IconVideo', | ||
| defaultRoleUniversalIdentifier: DEFAULT_ROLE_UNIVERSAL_IDENTIFIER, | ||
| settingsCustomTabFrontComponentUniversalIdentifier: '4ea804f4-6c22-457b-b8a2-66673bb6fc76', | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion | 🟠 Major Use the exported constant instead of a hardcoded UUID. The summary notes a ♻️ Proposed change import {
APP_DESCRIPTION,
APP_DISPLAY_NAME,
APPLICATION_UNIVERSAL_IDENTIFIER,
DEFAULT_ROLE_UNIVERSAL_IDENTIFIER,
+ SETTINGS_FRONT_COMPONENT_ID,
} from './constants/universal-identifiers';
@@
- settingsCustomTabFrontComponentUniversalIdentifier: '4ea804f4-6c22-457b-b8a2-66673bb6fc76',
+ settingsCustomTabFrontComponentUniversalIdentifier: SETTINGS_FRONT_COMPONENT_ID,🤖 Prompt for AI Agents |
||
| applicationVariables: { | ||
| MEETING_BAAS_API_KEY: { | ||
| universalIdentifier: 'c1d2e3f4-5a6b-7c8d-9e0f-a1b2c3d4e5f6', | ||
| description: 'Meeting BaaS API key for authenticating requests and verifying webhooks', | ||
| isSecret: true, | ||
| value: '', | ||
| }, | ||
| AUTO_CREATE_CONTACTS: { | ||
| universalIdentifier: '9637bafd-5888-4f34-bf8f-a4c82dbc4942', | ||
| description: 'Whether to auto-create contacts for unknown participants (true/false)', | ||
| value: 'true', | ||
| }, | ||
| }, | ||
| }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| export const APP_DISPLAY_NAME = 'Meeting BaaS Recorder'; | ||
| export const APP_DESCRIPTION = 'Record meetings via Meeting BaaS and sync recordings, transcripts, and participants into Twenty.'; | ||
| export const APPLICATION_UNIVERSAL_IDENTIFIER = 'b7e3a1f2-8d4c-4e6a-9f2b-1c5d7e8a3b4f'; | ||
| export const DEFAULT_ROLE_UNIVERSAL_IDENTIFIER = '3231cf40-5b90-4c2b-ae41-fcb5606299b4'; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| import { | ||
| defineField, | ||
| FieldType, | ||
| STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS, | ||
| } from 'twenty-sdk'; | ||
|
|
||
| export const BOT_ENTRY_MESSAGE_FIELD_ID = '5d9be02b-138b-5437-9184-d72276f51f3d'; | ||
|
|
||
| export default defineField({ | ||
| universalIdentifier: BOT_ENTRY_MESSAGE_FIELD_ID, | ||
| objectUniversalIdentifier: | ||
| STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.workspaceMember.universalIdentifier, | ||
| type: FieldType.TEXT, | ||
| name: 'botEntryMessage', | ||
| label: 'Bot Entry Message', | ||
| icon: 'IconMessage', | ||
| description: 'Message the bot posts in the meeting chat when it joins (max 500 characters)', | ||
| defaultValue: "''", | ||
|
Comment on lines
+17
to
+18
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Check if twenty-sdk FieldType.TEXT supports a maxLength/validation setting
rg -nP "FieldType\.TEXT|maxLength|universalSettings" --type=ts -g '!node_modules'Repository: Meeting-BaaS/twenty Length of output: 50375 🏁 Script executed: # First, read the actual field definition file to see current state
cat -n packages/twenty-apps/community/meeting-baas-recorder/src/fields/bot-entry-message-on-workspace-member.field.tsRepository: Meeting-BaaS/twenty Length of output: 791 🏁 Script executed: # Find schedule-bot.ts to check for validation logic
fd -i schedule-bot.ts --type fRepository: Meeting-BaaS/twenty Length of output: 151 🏁 Script executed: # Search for examples of TEXT field definitions with validation/maxLength settings
rg -A5 "FieldType\.TEXT" packages/twenty-apps --type=ts | head -50Repository: Meeting-BaaS/twenty Length of output: 3946 🏁 Script executed: # Check schedule-bot.ts to see if there's validation for the message length
cat packages/twenty-apps/community/meeting-baas-recorder/src/logic-functions/schedule-bot.tsRepository: Meeting-BaaS/twenty Length of output: 6253 🏁 Script executed: # Look for TEXT field definitions with universalSettings to see if maxLength is used
rg -B2 -A10 'type: FieldType\.TEXT' packages/twenty-apps --type=ts -g '*.object.ts|*.field.ts' | grep -A10 "universalSettings"Repository: Meeting-BaaS/twenty Length of output: 45 🏁 Script executed: # Check the FieldType definitions and TEXT field documentation
rg -B5 -A15 "export.*FieldType\.TEXT|TEXT.*FieldType" packages/twenty-sdk/src --type=tsRepository: Meeting-BaaS/twenty Length of output: 45 Add validation to enforce the 500-character limit or remove the documentation claim. The field description promises "max 500 characters" but the field definition lacks any length constraint in Either add a maxLength constraint to the field or add pre-flight validation in 🤖 Prompt for AI Agents |
||
| }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| import { | ||
| defineField, | ||
| FieldType, | ||
| STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS, | ||
| } from 'twenty-sdk'; | ||
|
|
||
| export const BOT_NAME_FIELD_ID = '6a37564a-25e6-5bdb-a119-1522e3817ae6'; | ||
|
|
||
| export default defineField({ | ||
| universalIdentifier: BOT_NAME_FIELD_ID, | ||
| objectUniversalIdentifier: | ||
| STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.workspaceMember.universalIdentifier, | ||
| type: FieldType.TEXT, | ||
| name: 'botName', | ||
| label: 'Bot Name', | ||
| icon: 'IconRobot', | ||
| description: 'Name displayed for the recording bot when it joins meetings', | ||
| defaultValue: "'Twenty CRM Recorder'", | ||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| import { | ||
| defineField, | ||
| FieldType, | ||
| RelationType, | ||
| OnDeleteAction, | ||
| STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS, | ||
| } from 'twenty-sdk'; | ||
| import { RECORDING_UNIVERSAL_IDENTIFIER } from '../objects/recording'; | ||
|
|
||
| export const CALENDAR_EVENT_ON_RECORDING_ID = '29fe48d1-7e7d-4253-9fea-0a876c2c116d'; | ||
| export const RECORDINGS_ON_CALENDAR_EVENT_ID = '131a78b1-f3c9-4b2e-9808-f9eb64bfb832'; | ||
|
|
||
| export default defineField({ | ||
| universalIdentifier: CALENDAR_EVENT_ON_RECORDING_ID, | ||
| objectUniversalIdentifier: RECORDING_UNIVERSAL_IDENTIFIER, | ||
| type: FieldType.RELATION, | ||
| name: 'calendarEvent', | ||
| label: 'Calendar Event', | ||
| icon: 'IconCalendarEvent', | ||
| relationTargetObjectMetadataUniversalIdentifier: | ||
| STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.calendarEvent.universalIdentifier, | ||
| relationTargetFieldMetadataUniversalIdentifier: RECORDINGS_ON_CALENDAR_EVENT_ID, | ||
| universalSettings: { | ||
| relationType: RelationType.MANY_TO_ONE, | ||
| onDelete: OnDeleteAction.SET_NULL, | ||
| joinColumnName: 'calendarEventId', | ||
| }, | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Test runner does not match the test files' imports.
The test files added in this package (e.g.,
src/receive-recording-webhook.test.ts) import fromvitest, but this config wires up Jest withts-jest. Jest will fail to resolvevitestat runtime. Please either:package.json'stestscript and deps accordingly), orvitestimports.See the paired comment on
src/receive-recording-webhook.test.ts.🤖 Prompt for AI Agents