495bd193a3ab29a11579c0ece37bdcce0cc05de3
37 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
65155fe50c |
feat(apps): add enqueueJob to run a logic function on the workers (#23527)
Closes twentyhq/core-team-issues#2742 A logic function run is capped by its own `timeoutSeconds` (900s max), so anything that can't finish in one run — a full re-sync, a per-record fan-out, a rate-limited third-party API — had no way to continue. This adds a way to hand that work to the workers. ## What it looks like for an app author ```ts import { enqueueJob } from 'twenty-sdk/logic-function'; await enqueueJob({ logicFunctionUniversalIdentifier: '9f1c3d7e-51b8-4a29-8f0d-7c4e2a6b1d33', payload: { cursor: nextCursor }, retryLimit: 3, priority: 2, delayMs: 60_000, }); ``` The target runs in its own process with its own timeout budget. The classic shape is a function that enqueues *itself* with the next cursor until there is nothing left. ## Changes **twenty-shared** — `EnqueueJobInput` / `EnqueueJobOptions` / `EnqueueJobResult` in `application`. **twenty-server** — new `application-job` module under `core-modules/application`, following the `application-key-value` pattern: - `enqueueJob` mutation on the metadata API, `@AuthApplication`-scoped - the lookup is scoped to `applicationId` + `workspaceId` — that's the authorization boundary, an app can only enqueue its own logic functions, anything else is `LOGIC_FUNCTION_NOT_FOUND` - pushes a `LogicFunctionTriggerJob` onto the existing `logicFunctionQueue`, so the enqueued run goes through the same executor (and the same execution throttling) as every other trigger - the queued run inherits the caller's `userId`/`userWorkspaceId`, so its app access token carries the same permissions as the function that queued it **Job options** are range-checked via `ResolverValidationPipe`, since the values come from application code and an unbounded delay or retry count would let an app pin work in the shared queue: | Option | Default | Range | |--------|---------|-------| | `retryLimit` | `0` | `0`–`10` | | `priority` | queue default | `1`–`10` (lower first) | | `delayMs` | `0` | `0`–7 days | `retryLimit` defaults to `0` rather than inheriting the server-route path's `3`: retries re-run the whole handler, so opting in should be the author's explicit choice. **twenty-sdk** — `enqueueJob` in `twenty-sdk/logic-function`, same shape as `runAgent`/`kv`. **Docs** — new "Background Jobs" page under Extend → Apps → Logic, plus nav and overview entries. **Generated** — regenerated `twenty-front/src/generated-metadata` and `twenty-client-sdk/src/metadata/generated` for the new mutation. ## Tests - `application-job.service.spec.ts` — 5 unit tests: job options mapping, defaults, acting-user propagation, application-scoped lookup, not-found - `enqueue-job.integration-spec.ts` — 5 integration tests: rejects a non-`APPLICATION_ACCESS` token, enqueues a function the app owns, rejects a function owned by another application, rejects an unknown identifier, rejects out-of-range options All green locally, along with `typecheck` for `twenty-server`/`twenty-sdk` and oxlint/oxfmt on the touched files. ## Notes for review - The target is addressed by `universalIdentifier`, matching `runAgent({ agentUniversalIdentifier })` and `ServerRouteDispatchResult.targetLogicFunctionUniversalIdentifier`. Addressing by `name` would be friendlier, but logic function names aren't validated for uniqueness within an app — happy to add it as a convenience if you'd rather. - `enqueueJob` returns as soon as the job is accepted; it can't return the target's result, since the queue driver's `add` returns void. Documented, with a pointer to the KV store for handing results back. --- _Generated by [Claude Code](https://claude.ai/code/session_01QrYvGonS3HMdeuMAVjs5hR)_ <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/23527?utm_source=github" target="_blank" rel="noopener noreferrer" data-no-image-dialog="true"><picture><source media="(prefers-color-scheme: dark)" srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img alt="Review in cubic" src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a> <!-- End of auto-generated description by cubic. --> --------- Co-authored-by: github-actions <github-actions@twenty.com> |
||
|
|
25f28ee299 |
Fix TWENTY-SERVER-60Y: register permissions exception filter globally (#23104)
## Context Sentry [TWENTY-SERVER-60Y](https://twenty-v7.sentry.io/issues/6633503406) ("Permission Denied: Entity performing the request does not have permission") has ~12.9k occurrences / 151 users. It groups by the shared throw site `settings-permission.guard.ts:57`, so it's a **catch-all bucket** for settings-permission denials across many resolvers, not a single operation. Sampled events include `findOneApplication` (app runtimes reading their own `applicationVariables`), `uploadFilesFieldFileByUniversalIdentifier`, `UpdatePageLayoutWithTabsAndWidgets`, `CreateFileUpload`, etc. ## Root cause Settings-permission denials are only converted to a client-appropriate `FORBIDDEN` when a resolver manually attaches `PermissionsGraphqlApiExceptionFilter`. **28** GraphQL resolvers do; **~35** guarded resolvers do not. On those, the guard-thrown `PermissionsException` (a `CustomException`, not a `BaseGraphQLError`) falls through to the Yoga error hook, is serialized as `INTERNAL_SERVER_ERROR`, and `shouldCaptureException` reports it to Sentry as a 500-class error. REST is not affected: every `SettingsPermissionGuard` controller already carries `PermissionsRestApiExceptionFilter` (15/15). ## Fix Register `PermissionsGraphqlApiExceptionFilter` globally via `APP_FILTER` in the core and metadata engine modules, mirroring the existing global GraphQL filters `BillingGraphqlApiExceptionFilter` and `FlatEntityMapsGraphqlApiExceptionFilter`. The filter is guarded on the GraphQL context (`host.getType()`), so REST keeps its existing per-controller filter untouched and no request-scoped dependency is pulled into a global provider. Every settings-guarded resolver now returns `FORBIDDEN` for denials, which is both the correct client error code and excluded from Sentry. Existing per-resolver `@UseFilters(PermissionsGraphqlApiExceptionFilter)` entries remain valid (handler-scoped takes precedence, identical result); collapsing them into the global registration is a possible follow-up. ## Test Added a case to `granular-settings-permissions.integration-spec.ts`: a member without the `APPLICATIONS` flag calling `findOneApplication{applicationVariables{key value}}` (the exact Sentry query, and a resolver with **no** resolver-scoped filter) must receive `FORBIDDEN`, exercising the global filter. Verified against the local test DB: - With the global filter: passes (`FORBIDDEN`). - Without it: fails with `INTERNAL_SERVER_ERROR`, reproducing the leak. - The existing roles / workspace-members / api-keys denial tests (per-resolver filters) still pass, confirming no precedence conflict and no boot issue from dual `APP_FILTER` registration. Fixes TWENTY-SERVER-60Y |
||
|
|
4c4a154d31 |
key-value storage for applications (#23089)
## What
Key-value storage for applications, as proposed in
twentyhq/core-team-issues#2391 — built on the existing `keyValuePair`
entity:
- Nullable `applicationId` relation on `keyValuePair` + a new
`APPLICATION_VARIABLE` type (fast instance command included)
- GraphQL CRUD on the metadata schema (`appKeyValue`, `setAppKeyValue`,
`deleteAppKeyValue`), requiring an `APPLICATION_ACCESS` token —
`applicationId` always comes from the token, never from arguments, so
apps can't touch each other's entries
- `kv.get` / `kv.set` / `kv.delete` helpers in
`twenty-sdk/logic-function`
## Scopes
- **`INSTALL`** (default): entries are private to one workspace install;
arbitrary JSON values
- **`GLOBAL`**: entries are shared across every install of the app, with
claim semantics — the value is always the claiming `workspaceId` and
only that workspace can overwrite or delete the key (guarded writes,
race-safe via insert-if-absent)
Since `applicationId` identifies an install (one row per workspace),
GLOBAL entries are stored under the registration owner workspace's
install so all installs of the same app share one namespace.
The GLOBAL scope is what enables cross-workspace webhook routing: e.g.
the Slack app's `serverRoute` resolver (running in the owner workspace)
can resolve `kv.get('slack:team:' + team_id, { scope: 'GLOBAL' })` to
find the workspace that connected that Slack team — without a workspace
being able to hijack another's mapping.
## Follow-ups
- Wire the Slack assistant PR (#22984) to write the claim at connect
time and read it in the events resolver
- `kv.*` access from front components
<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/23089?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
|
||
|
|
b5a1aed24b |
feat(server): run server-exposed logic functions in the owner workspace (#22002)
## Summary Implements the server-level logic-function tier in the simplest shape: a logic function is "server-exposed" iff its manifest entry carries `serverWebhookTriggerSettings`. Execution delegates to the owner-workspace copy of that function — billing, throttling, env vars, and the existing executor all apply uniformly against that workspace. Supersedes #21971 with the simplified design from that discussion (no `applicationRegistrationLogicFunction` registry, no dedicated manifest type, no separate SDK helper, no special throttling). ## Design - **Manifest**: `LogicFunctionManifest` gains `serverWebhookTriggerSettings?`. The declarative `workspaceIdResolver` shape is dropped. - **Materialization**: those settings become two new jsonb columns on `LogicFunctionEntity`. The manifest → flat converter and the create-from-source DTO/util forward them; the property-config map and editable-properties list are extended. - **Lookup**: a single QB query joins `logicFunction → application → applicationRegistration` and filters on `lf.workspaceId = reg.workspaceId` to get only the owner workspace's copy. - **Webhook**: `POST /webhooks/server/:logicFunctionUniversalIdentifier` → `ServerWebhookTriggerService.handle` → join lookup → `LogicFunctionTriggerService.run`. No registry table, no `:applicationRegistrationUniversalIdentifier` segment, no resolver. - **Gate**: `IS_SERVER_LOGIC_FUNCTION_ENABLED` config var (disabled by default). ## Test plan - [x] `npx jest server-webhook-trigger` — 9 unit tests across the webhook service. - [x] `npx jest logic-function` — 88 existing tests stay green. - [x] `npx nx typecheck twenty-server`. - [x] `npx nx lint:diff-with-main twenty-server`. - [x] Reset DB → init → run `database:migrate:prod` → run `database:migrate:generate --name pending-migration-check` → no drift. - [ ] Manual: hit `/webhooks/server/<uid>` end-to-end against a manifest carrying `serverWebhookTriggerSettings`. https://claude.ai/code/session_01GgsnCGmYJ26xRirx8va1Yh --- _Generated by [Claude Code](https://claude.ai/code/session_01GgsnCGmYJ26xRirx8va1Yh)_ <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/22002?utm_source=github" target="_blank" rel="noopener noreferrer" data-no-image-dialog="true"><picture><source media="(prefers-color-scheme: dark)" srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img alt="Review in cubic" src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a> <!-- End of auto-generated description by cubic. --> |
||
|
|
6423c4cd3c |
Add recall io webhook endpoint (#21879)
## Context
Bot-recording integrations (e.g. the Recall.ai meeting bot) receive
webhooks from a third-party provider that delivers **every
tenant's events to a single URL**. Our existing `route-trigger` (`/s/…`)
resolves the workspace from the request host, which can't
work for one shared multi-tenant webhook URL. We need an instance-scoped
ingress that identifies the target workspace from the payload
instead.
## Strategy
Add a new **`ingress-trigger`** logic-function trigger, mirroring
`route-trigger`:
- A public endpoint keyed by the app's identifiers: `POST
/webhooks/ingress/:applicationRegistrationUniversalIdentifier/:logicFunctionUniversalIdentifier`.
- The logic function declares an `ingressTriggerSettings` block in its
manifest describing how to find the workspace in the payload
(`workspaceId: { source: 'body' | 'query' | 'header', path }`).
- Core only **resolves the workspace** (declarative, fail-closed,
prototype-safe path getter), verifies the app is installed in that
workspace, then runs the function **synchronously** so the provider sees
the response (status codes / retries).
- **Signature verification stays in the logic function** (it gets
`rawBody` + forwarded headers), keeping core provider-agnostic.
- Shared execution logic (`build event → execute → map response`)
extracted into `LogicFunctionTriggerService`, now reused by both
`route-trigger` and `ingress-trigger`.
## Major changes
- **twenty-shared**: new `ingressTriggerSettings` on
`LogicFunctionManifest` (`IngressTriggerSettings` type).
- **twenty-server**: new `ingress-trigger` module (controller, service,
exception + filter, workspace-id resolver util).
- **twenty-server**: extracted `LogicFunctionTriggerService` +
`route-trigger-response.util` (response builder + sender); refactored
`RouteTriggerService` and both controllers to reuse them.
- **twenty-docs**: documented the ingress trigger (endpoint, workspace
resolution, signature responsibility, provider HMAC examples).
- Unit tests for the resolver and the ingress service.
|
||
|
|
c2ca90c255 |
feat(sdk): add runAgent() to run app agents from logic functions (#21157)
<img width="948" height="593" alt="image" src="https://github.com/user-attachments/assets/d990fa98-3cfd-469d-ab7f-0b2d4ccf3afc" /> <img width="1361" height="802" alt="image" src="https://github.com/user-attachments/assets/1091f598-49f3-4c16-92ea-1e1c200181e2" /> ## Add `runAgent()` to the Logic Function SDK Lets an app's logic function run one of its own AI agents server-side and get the result back synchronously — reusing the existing agent executor instead of a new bespoke transport. ### Backend - New **`runAgent` GraphQL mutation** (metadata schema) in `ai-agent-execution`, wrapping the existing `AgentAsyncExecutorService.executeAgent`. Scopes the agent lookup to the calling application and runs it under an application auth context. - New `@AuthApplication()` param decorator (mirrors `@AuthWorkspace()`) — first GraphQL resolver authenticated by an **application access token**. - Guarded by `WorkspaceAuthGuard` + `SettingsPermissionGuard(PermissionFlagType.AI)`: the app's role must grant the `AI` permission flag. ### SDK - `runAgent({ agentUniversalIdentifier, prompt })` posts the mutation to `/metadata` with the app token via a new runtime GraphQL transport. Returns `{ result, hasNoMoreAvailableCredits }`. - Refactored the connections helpers onto a shared `postAppEndpoint` util (removes duplicated transport logic). ### Frontend - App install permission modal now shows an explicit consent line — _"Run AI agents and bill AI credits to your workspace"_ — when the app's role requests the `AI` flag. ### Docs - Documented `runAgent` and its `AI` permission-flag requirement in _Skills & Agents_. - Fixed outdated role-permission examples in _Roles & Permissions_ (`permissionFlags` → `permissionFlagUniversalIdentifiers`, `PermissionFlag` → `SystemPermissionFlag`). ### Test plan - [x] SDK unit tests (`run-agent.spec.ts`) — request shape, GraphQL/HTTP error handling, missing env vars - [x] `twenty-server`, `twenty-front`, `twenty-shared` typecheck + lint - [ ] Manual: install an app granting the `AI` flag, call `runAgent()` from a logic function, confirm the agent runs and credits are billed --------- Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com> |
||
|
|
b338a7a1d2 |
feat(settings): discovery hero rollout + ephemeral playground token (#21072)
## Summary
Two intertwined streams of work:
### UI — discovery hero pattern, settings shell, AI/API redesign
- **Generalize `SettingsDiscoveryHeroCard`** and use it on Layout, Data
Model, Apps, AI, API/Webhooks, Members. Drops 4 per-page wrapper files
(`SettingsObjectCoverImage`, `SettingsLayoutCoverImage`,
`SettingsLayoutCustomizeVideoModal`,
`SettingsDataModelVisualizeVideoModal`). Each page now supplies cover
src, modal id, and tab list.
- **Modal**: swap `<video>` placeholder for the Vimeo iframe pattern
from `twenty-docs`, per-tab `vimeoId`. Drop the parallel border-bottom
on the header (TabList draws its own baseline) and the grey background
behind the video. Note: Vimeo's embed allowlist applies — the iframes
load with the correct URL on `localhost` but the player itself requires
the video owner to allow the dev/staging domains in Vimeo settings.
- **AI page** rebuilt into a Cockpit pattern (Overview / Models / Skills
/ Tools / Usage). New `SettingsAiOverviewTab` with default Smart/Fast
pickers, at-a-glance stats, and an MCP signpost that deep-links to
`/settings/api-webhooks#mcp`. System Prompt link moved under Models.
Advanced tab removed.
- **API & Webhooks** now has 4 tabs (Playground / MCP / API Keys /
Webhooks). Hero card above tabs. Playground tab inverted to "Core API" /
"Metadata API" sections, each containing REST + GraphQL cards — schema
is the meaningful axis, protocol is secondary. Hash deep-link sync
delegated to the shared `TabListFromUrlOptionalEffect`.
- **Settings shell**: unified drawer outer padding (kill `isSettings`
branch), extract `CollapsibleNavigationDrawerSection`, add `iconColor`
on settings nav items, fix Exit Settings button alignment, 880px content
cap.
### Backend — strategy C: ephemeral playground token
The legacy paste-your-API-key flow is replaced by an on-demand
short-lived token scoped to the calling user's permissions. No shared
"Playground" API key to manage or revoke.
- New `JwtTokenTypeEnum.PLAYGROUND`. `PlaygroundTokenJwtPayload =
Omit<AccessTokenJwtPayload, 'type' | impersonation fields>` so any
future ACCESS claim flows through automatically.
- `AccessTokenService.generatePlaygroundToken` signs an access-shaped
JWT with `type: PLAYGROUND` and a configurable short TTL. A shared
private `resolveTokenSubject` helper parallelizes the user / workspace /
userWorkspace lookups for both generators.
- `JwtAuthStrategy.validateAccessToken` widened to accept
`AccessTokenJwtPayload | PlaygroundTokenJwtPayload`; impersonation gated
on `payload.type === ACCESS` so the union narrows without `as unknown
as` casts. The two branches in `validate()` collapse into one.
- New `PLAYGROUND_TOKEN_EXPIRES_IN` config var (default `2h`).
- New `generatePlaygroundToken` mutation (`WorkspaceAuthGuard`, no args,
returns `AuthToken`).
- Frontend `useOpenPlayground` hook centralizes mint → atom write →
navigate, with Apollo `onError` snackbar and a "use cached PLAYGROUND
token if still fresh" short-circuit (decodes via `jwt-decode`, checks
both `type` AND `exp`). Old API_KEY tokens left in localStorage from the
prior paste-form flow are rejected on `type` alone and force a re-mint —
this is what was causing the "This API Key is revoked" symptom on stale
browsers.
### Drive-by cleanups
- `PlaygroundToken` DTO removed (identical shape to `AuthToken` already
in use).
- 5 `customize-sidebar.webm` imports and the dead placeholder pipeline
removed.
## Test plan
### Discovery hero
- [ ] `/settings/layout`, `/settings/data-model`,
`/settings/applications`, `/settings/ai`, `/settings/api-webhooks`,
`/settings/members` each render the discovery hero card with its
illustration + play button + tabbed modal
- [ ] Modal tabs show the correct Vimeo embed URL per tab; aspect ratio
stays at 1440/900; no parallel border-bottom jog at the tab baseline
- [ ] AI Overview tab shows Smart/Fast model pickers + stats grid + MCP
signpost card; the MCP card lands on `/settings/api-webhooks#mcp` with
the MCP tab active
### API playground (ephemeral token)
- [ ] With an empty `playgroundApiKeyState` in localStorage, clicking
REST or GraphQL playground card opens the playground and the cached
token has `type: "PLAYGROUND"` with ~2h exp
- [ ] Clicking the card again within the freshness window does **not**
re-mint (`iat` / fingerprint stable across visits)
- [ ] Planting a fake API_KEY-shaped JWT in localStorage and clicking
the card forces a fresh mint (old token rejected on `type`)
- [ ] `GET /rest/companies?limit=1` with the cached token returns 200 +
real data
- [ ] `POST /graphql { __typename }` returns 200
### Settings shell
- [ ] Settings nav matches main app drawer padding; sections collapse;
Exit Settings button aligns with the workspace links above
- [ ] Active nav items have a right-gap (cleaner active state)
- [ ] Content area capped at 880px
### Verify
- [ ] `npx nx typecheck twenty-front` passes
- [ ] `npx nx typecheck twenty-server` passes
- [ ] `npx nx lint:diff-with-main twenty-front` passes
- [ ] `npx nx lint:diff-with-main twenty-server` passes
|
||
|
|
db0547f503 |
[1/3] Rename permissionFlag to rolePermissionFlag + add permissionFlag catalog/backfill (#20481)
Split of #20377. ## Summary This PR separates available permission flags from per-role permission flag grants. Previously, `core.permissionFlag` stored the role assignment directly: `roleId + flag`. This PR renames that legacy grant table to `core.rolePermissionFlag`, then recreates `core.permissionFlag` as the catalog of available permission flags. ## What changed - Rename the existing `core.permissionFlag` grant table to `core.rolePermissionFlag`. - Add the new syncable `core.permissionFlag` catalog entity with key, label, description, icon, permission type, relevance flags, and custom/standard metadata. - Add stable `SystemPermissionFlag` universal identifiers for the built-in `PermissionFlagType` values. - Seed the standard permission flags for every workspace under the Twenty standard application. - Backfill existing role grants: - create missing catalog rows for existing grant keys, - add `rolePermissionFlag.permissionFlagId`, - migrate grants from the old string `flag` column to the new catalog FK, - replace the old `(flag, roleId)` uniqueness with `(permissionFlagId, roleId)`. - Rewire role permission flag caches, permission checks, role DTO mapping, and `upsertPermissionFlags` to resolve through the catalog. - Keep the existing public role permission API shape: product/app surfaces still talk about `permissionFlags` and return `{ id, roleId, flag }`. - Update metadata flat-entity machinery, migration builders, validators, action handlers, snapshots, generated schemas, docs, and app fixtures for the new `permissionFlag` / `rolePermissionFlag` split. ## Behavior after this PR - Existing permission flag grants keep working. - Existing GraphQL role permission flows keep the same public naming. - Standard permission flags are represented as catalog rows. - Permission checks now compare grants through catalog universal identifiers instead of the legacy `flag` column. - Workspace deletion cleanup now verifies both `permissionFlag` and `rolePermissionFlag`. ## What is not in this PR - Public GraphQL CRUD for custom permission flags. - App manifest support for declaring new custom permission flags. - Frontend UI for creating or assigning custom permission flags beyond the existing role permission flow. --------- Co-authored-by: Weiko <corentin@twenty.com> |
||
|
|
f6423f5925 |
Remove DataSourceService and clean up datasource migration logic (#19532)
## Summary - **Drop the `objectMetadata.dataSourceId` foreign key and index** via a 1-22 fast instance command — column kept nullable for data preservation - **Delete `DataSourceService`, `DataSourceModule`, and `DataSourceException`** — all code now uses `workspace.databaseSchema` directly - **Remove `IS_DATASOURCE_MIGRATED` feature flag** from default flags and all branching logic - **Simplify workspace/object creation pipelines** — `WorkspaceManagerService`, `DevSeederService`, and the object creation action handler no longer route through `DataSourceService` - **Keep `DataSourceEntity` and the `dataSource` table** for historical data — entity stripped of all ORM relations |
||
|
|
cee4cf6452 |
feat: migrate ConnectedAccount infrastructure entities to metadata schema (#18784)
## Summary - Migrates 4 entities (`connectedAccount`, `messageChannel`, `calendarChannel`, `messageFolder`) from per-workspace schemas to the shared `core` metadata schema - Introduces a `IS_CONNECTED_ACCOUNT_MIGRATED` feature flag to control the migration: when enabled, reads come from core metadata and all writes are dual-written to both workspace and core - Extracts 12 enums from workspace entity files to `twenty-shared` for reuse across frontend and backend - Creates new TypeORM entities, metadata services, GraphQL resolvers/DTOs, and exception interceptors per entity - Each entity owns its own data access module (`ConnectedAccountDataAccessModule`, `MessageChannelDataAccessModule`, `CalendarChannelDataAccessModule`, `MessageFolderDataAccessModule`) — no umbrella infrastructure module - Adds a 1.20 upgrade command that backfills data from workspace schemas to core (preserving UUIDs) and enables the feature flag - Replaces direct repository access with data access service calls across ~50 files in messaging, calendar, and connected-account modules - Adds `lastSignedInAt` and `oidcTokenClaims` fields to the new `ConnectedAccountEntity` - Drops unused `lastSyncHistoryId` field from the migrated connected account entity ## Test plan - [x] Lint passes (`npx nx lint:diff-with-main twenty-server`) - [x] Typecheck passes (`npx nx typecheck twenty-server`) - [x] All unit tests pass (477 suites, 4267 tests, 0 failures) - [ ] Manual test: verify messaging sync works with feature flag disabled (existing behavior) - [ ] Manual test: run upgrade command on a workspace, verify data backfilled to core tables - [ ] Manual test: verify messaging/calendar sync works with feature flag enabled (dual-write path) - [ ] Manual test: verify GraphQL metadata resolvers return correct data when flag enabled |
||
|
|
7a3540788a |
feat: uniformize metadata store with flat types, SSE alignment, presentation endpoint & localStorage (#18647)
## Summary
Uniformizes the metadata store to support **all** backend flat metadata
types, introduces a **minimal metadata endpoint** for fast initial
renders, replaces custom localStorage persistence with **Jotai's
built-in `atomWithStorage`**, and wires up a
**MinimalMetadataLoadEffect** for stale-while-revalidate loading.
### Key changes
- **All flat metadata types**: Added `FlatCommandMenuItem`,
`FlatFrontComponent`, `FlatWebhook`, `FlatRole`, `FlatRoleTarget`,
`FlatAgent`, `FlatSkill`, `FlatRowLevelPermissionPredicate`,
`FlatRowLevelPermissionPredicateGroup` — every entity in the backend
`MetadataEntityTypeMap` now has a corresponding frontend flat type
registered in `ALL_METADATA_ENTITY_KEYS` and `MetadataEntityTypeMap`.
- **Minimal metadata endpoint** (`minimalMetadata` GraphQL query): New
backend module (`MinimalMetadataModule`) returns lightweight object
metadata (names, icons, labels, flags) and basic views (id, type, key,
objectMetadataId) plus a `metadataVersion`. This enables fast first
paint before full metadata loads.
- **Jotai `atomWithStorage` for persistence**: Replaced the custom
`MetadataLocalStorageEffect` with Jotai's built-in `atomWithStorage` on
both `metadataStoreState` (family) and `metadataVersionState`. Added
`localStorageOptions` support to `createAtomFamilyState` for `{
getOnInit: true }` synchronous hydration. Each entity atom auto-persists
under keys like `metadataStoreState__objectMetadataItems`.
- **MinimalMetadataLoadEffect**: New effect mounted before
`MetadataProviderInitialEffects` that checks if the store already has
data (from Jotai localStorage hydration). If empty, it fetches minimal
metadata from the new endpoint. The full metadata load continues in
parallel, eventually enriching the store with complete data.
- **SSE effects alignment**: All metadata entity types now have
corresponding SSE effects that directly patch the metadata store via
`patchMetadataStoreFromSSEEvent`.
- **Existing selectors and joining logic**:
`objectMetadataItemsWithFieldsSelector`, `viewsWithRelationsSelector`,
`pageLayoutsWithRelationsSelector` reconstruct nested data from flat
entities for components that need it.
### Loading flow
```
App mount
→ Jotai atomWithStorage hydrates store from localStorage (sync, getOnInit)
→ MinimalMetadataLoadEffect
→ Store has data? → skip (app renders immediately)
→ Store empty? → fetch minimalMetadata endpoint → populate objects + views
→ MetadataProviderInitialEffects (full metadata load, runs in parallel)
→ LazyMetadataLoadEffect (page layouts, logic functions, nav menu, etc.)
→ IsAppMetadataReadyEffect (sets isAppMetadataReady)
```
## Test plan
- [ ] Verify app loads with empty localStorage (should fetch minimal
metadata, then full)
- [ ] Verify app loads with populated localStorage (should skip minimal
fetch, render immediately)
- [ ] Verify SSE events correctly update metadata store for all entity
types
- [ ] Verify logout clears metadata store (atom reset propagates to
localStorage)
- [ ] Verify all metadata selectors return correct joined data
- [ ] CI: lint, typecheck, tests pass
|
||
|
|
845a1934d3 |
Tt call recording app (#18281)
Co-authored-by: Thomas Trompette <thomas.trompette@sfr.fr> Co-authored-by: bosiraphael <raphael.bosi@gmail.com> Co-authored-by: Weiko <corentin@twenty.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions <github-actions@twenty.com> Co-authored-by: Charles Bochet <charles@twenty.com> |
||
|
|
3dc5b162c7 |
Spread in parent and requires FlatEntity.__universal (#17753)
# Introduction Requiring the spreaded `__universal` record that aggregates all the universal identifier ( relations fk and aggregators ) of an entity to its root It's blockin for https://github.com/twentyhq/twenty/pull/17687 to be finalized because if we don't we would have to migrated all related entities at once in order for them to always have the universal properties ## `resolveEntityRelationUniversalIdentifiers` Introduced `resolveEntityRelationUniversalIdentifiers` a centralized utility that resolves foreign key IDs to universal identifiers using ALL_METADATA_RELATIONS metadata. It provides strict typing for both input (foreign keys) and output (universal identifiers), with nullability dynamically inferred from entity relation types. Strictly and dynamically typed for both output and input To do so added a new type and const/runtime grain to ALL_METADATA_RELATIONS `isNullable`to many-to-one entries, derived from the entity relation property types. And fixed incorrectly typed typeorm entities ### Usage ```ts const { availabilityObjectMetadataUniversalIdentifier, frontComponentUniversalIdentifier, } = resolveEntityRelationUniversalIdentifiers({ metadataName: 'commandMenuItem', foreignKeyValues: { availabilityObjectMetadataId: createCommandMenuItemInput.availabilityObjectMetadataId, frontComponentId: createCommandMenuItemInput.frontComponentId, }, flatEntityMaps: { flatObjectMetadataMaps, flatFrontComponentMaps }, }); ``` |
||
|
|
da6f1bbef3 |
Rename serverlessFunction to logicFunction (#17494)
## Summary Rename "Serverless Function" to "Logic Function" across the codebase for clearer naming. ### Environment Variable Changes | Old | New | |-----|-----| | `SERVERLESS_TYPE` | `LOGIC_FUNCTION_TYPE` | | `SERVERLESS_LAMBDA_REGION` | `LOGIC_FUNCTION_LAMBDA_REGION` | | `SERVERLESS_LAMBDA_ROLE` | `LOGIC_FUNCTION_LAMBDA_ROLE` | | `SERVERLESS_LAMBDA_SUBHOSTING_URL` | `LOGIC_FUNCTION_LAMBDA_SUBHOSTING_URL` | | `SERVERLESS_LAMBDA_ACCESS_KEY_ID` | `LOGIC_FUNCTION_LAMBDA_ACCESS_KEY_ID` | | `SERVERLESS_LAMBDA_SECRET_ACCESS_KEY` | `LOGIC_FUNCTION_LAMBDA_SECRET_ACCESS_KEY` | ### Breaking Changes - Environment variables must be updated in production deployments - Database migration renames `serverlessFunction` → `logicFunction` tables |
||
|
|
a4499d21bb |
Migrate cron, databaseEventTrigger, httpRoute triggers to serverless functions (#17488)
## Summary Migrates trigger entities (`CronTriggerEntity`, `DatabaseEventTriggerEntity`, `RouteTriggerEntity`) into `ServerlessFunctionEntity` by storing trigger settings as JSONB columns directly on the serverless function. This simplifies the architecture since these relationships were effectively one-to-one. ## Changes ### Schema Changes - Added three new nullable JSONB columns to `ServerlessFunctionEntity`: - `cronTriggerSettings` - stores cron pattern - `databaseEventTriggerSettings` - stores event name and updated fields filter - `httpRouteTriggerSettings` - stores path, HTTP method, auth requirements, and forwarded headers ### Core Logic Updates - `CronTriggerCronJob` - now queries `ServerlessFunctionEntity` directly instead of `CronTriggerEntity` - `CallDatabaseEventTriggerJobsJob` - now queries `ServerlessFunctionEntity` directly - `RouteTriggerService` - now queries `ServerlessFunctionEntity` directly - `ApplicationSyncService` - extracts trigger settings from manifest and writes to serverless function |
||
|
|
bc7791871f |
Introduce webhook v2 (#17456)
Migrate webhook to v2 entity |
||
|
|
aff577689f |
Add syncable NavigationMenuItem entity to core schema (#17232)
Implements a new syncable `navigationMenuItem` entity in the core schema to replace the workspace `favorite` entity. ## Next Steps - Frontend integration ([separate PR](https://github.com/twentyhq/twenty/pull/17268)) - Data migration (separate PR) |
||
|
|
5a617e4718 | Add command menu item entity (#17181) | ||
|
|
5fc4e810f7 |
Front Extensibility: Introduce Front Component Entity (#17175)
As part of the extensibility effort, we are introducing a new engine entity called "Front Component". This represents a dynamic react component that will be rendered in CommandMenu actions or in PageLayout widgets This PR introduce the entity and all the necessary boilerplate to make it syncable and cachable in the engine |
||
|
|
942d2fef83 |
Remove sync-metadata and IS_WORKSPACE_CREATION_V2_ENABLED feature flag (#16997)
# Introduction Followup of https://github.com/twentyhq/twenty/pull/17001#pullrequestreview-3638508738 close https://github.com/twentyhq/core-team-issues/issues/1910 We've completely decom the `sync-metadata` in production. We're now then removing its implementation in favor of the v2. ## TODO: - [x] Remove sync-metadata implem and commands - [x] Remove workspace decorators - [x] Type each deprecated field to deprecated on their workspaceEntity - [x] Remove the `workspace-sync-metadata` folder entirely - [x] remove workspace migration - [x] workspace migration removal migration - [x] remove the `v2` references from workspace manager file names - [x] remove the `v2` references from workspace manager modules - [ ] Double check impact on translation file path updates ## Note - Removed the gate logic - Remains some service v2 naming, serverless needs to be migrated on v2 fully - Removed workspaceMigration service app health consumption, making it always returning up ( no more down ) cc @FelixMalfait ( quite obsolete health check now, will require complete refactor once we introduce inter app dependency etc ) |
||
|
|
66b2882ac4 |
BREAKING CHANGE: Removing remote integration feature (#17001)
## Context The feature has not been maintained for more than a year and was never officially launched. This PR removes its code due to the upcoming refactoring of the sync-metadata that will break its logic if not handled properly and it would be too costly for the time being. TODO: - remove isRemote from object/field metadata |
||
|
|
21ff42074d |
feat: implement skills system for AI agents (#16865)
## Summary This PR introduces a Skills system for AI agents, inspired by the [Agent Skills specification](https://agentskills.io/specification). ## Changes ### Backend - **SkillEntity**: New database entity with migration for storing skills - **V2 Sync Mechanism**: Implemented FlatSkill, builders, validators, and action handlers following the v2 flat entity pattern - **Standard Skills**: Pre-defined skills (workflow-building, data-manipulation, dashboard-building, metadata-building, research, code-interpreter, xlsx, pdf, docx, pptx) - **GraphQL API**: CRUD operations for skills with proper guards and permissions - **Workspace Cache**: Integrated skills into the workspace cache system ### Frontend - **Skills Table**: Searchable table in AI settings showing all skills - **Skill Form**: Create/edit page with Label (primary), Description, and Content (markdown editor) - **API Name**: Following existing patterns, name is derived from label with advanced settings toggle for custom API names - **Standard vs Custom**: Standard skills are read-only, custom skills can be edited/deleted ## Key Design Decisions - Skills are stored in the database (Salesforce-like approach) rather than files - Name is derived from Label by default (isLabelSyncedWithName pattern) - Skills reference functions/files via @ mentions in markdown content rather than explicit relations - Standard skills are synced from code, custom skills are created via UI ## Screenshots Skills table and form UI follow existing settings patterns. ## Testing - [x] Lint passes - [x] Typecheck passes - [ ] CI tests |
||
|
|
4f20fd35c5 |
feat: Add Agent Evaluation System and Refactor AI Modules (#16111)
## Summary This PR introduces a comprehensive agent evaluation system and refactors the AI module structure for better organization. ## Key Changes ### 🎯 Agent Evaluation System - Added **Agent Turn Evaluation** entities, DTOs, and database schema - New GraphQL mutations: `evaluateAgentTurn` and `runEvaluationInput` - Added `evaluationInputs` field to Agent entity for storing test inputs - New `AgentTurnGraderService` for automatic turn evaluation - Added evaluation UI with new **Evals** and **Logs** tabs in agent detail pages ### 🏗️ Entity & Module Refactoring - Renamed `AgentChatMessage` → `AgentMessage` for clarity - Consolidated chat entities: `AgentMessage`, `AgentTurn`, and `AgentChatThread` - Reorganized AI modules under `ai/` subdirectory structure - Updated imports across codebase to reflect new module paths ### 🤖 New Agents & Roles - Added **Dashboard Builder Agent** for dashboard creation and management - Added **Dashboard Manager Role** with appropriate permissions - Updated role permissions to be more granular (users vs agents vs API keys) ### 🔐 Permission System Updates - Added `HTTP_REQUEST_TOOL` permission flag - Updated Workflow Manager role permissions (restricted tool access) - Enhanced permission flag types to differentiate between user/agent/API key contexts - Added `isRelevantForAgents`, `isRelevantForApiKeys`, `isRelevantForUsers` to permission flags ### 📨 Message Role Enhancement - Added `system` role to `AgentMessageRole` enum (alongside user/assistant) - Updated message handling to support system prompts ### 🎨 UI/UX Improvements - New tabs in agent detail: **Evals** and **Logs** - Added turn detail page: `/ai/agents/:agentId/turns/:turnId` - Fixed text overflow in `SettingsListItemCardContent` - Updated role applicability labels ("Assignable to Workspace Members") ### 🛠️ Technical Improvements - Fixed Zod schema validation for UUID and Date fields (use string validators) - Updated `ToolRegistryService` to properly register HTTP tool with permission flag - Enhanced error handling in agent execution services - Updated database migrations for new entity schema ## Database Migrations - `1764210000000-add-system-role-to-agent-message.ts` - `1764220000000-add-evaluation-inputs-to-agent.ts` - `1764200000000-add-agent-turn-evaluation.ts` - `1764100000000-refactor-agent-chat-entities.ts` ## Testing - [ ] Agent evaluation flow tested - [ ] Dashboard Builder agent tested - [ ] Permission system validated - [ ] UI tabs and navigation tested - [ ] Database migrations run successfully ## Breaking Changes ⚠️ **Entity Rename**: `AgentChatMessage` renamed to `AgentMessage` - GraphQL queries need updating ## Related Issues <!-- Link any related issues here --> ## Screenshots <!-- Add screenshots if applicable --> |
||
|
|
e7ebf51e50 |
Replace agent handoff system with planning-based router (#16003)
## Overview This PR replaces the dynamic agent handoff system with a more predictable planning-based router that decides upfront how to handle multi-agent coordination. ## Major Changes ### 🔄 Architecture Shift: Handoffs → Planning **Removed:** - `AgentHandoffEntity` and handoff tracking system - `AgentHandoffService` and `AgentHandoffExecutorService` - Dynamic agent-to-agent transfers during execution - Handoff tool generation and description templates **Added:** - `AiRouterService` with two strategies: `simple` (single agent) and `planned` (multi-agent) - `AgentPlanExecutorService` for executing multi-step plans - Plan validation (cycle detection, dependency resolution) - `UnifiedRouterResult` type with discriminated union ### 🤖 New Standard Agents Added two new specialized agents: - **Researcher Agent**: Web search, fact-finding, competitive intelligence - **Code Agent**: TypeScript function generation for serverless workflows ### 🏗️ Router Refactoring (Latest) Split router responsibilities into focused services: - `AiRouterStrategyDeciderService`: Decides simple vs planned strategy - `AiRouterPlanGeneratorService`: Generates and validates execution plans - `AiRouterService`: Coordinates between services (reduced from 426→275 lines) ### ⚙️ Configuration Improvements - Added `outputStrategy` to agent definitions (`direct` vs `synthesize`) - Removed hardcoded special cases for workflow-builder - Added `plannerModel` field to workspace entity - Increased `MAX_STEPS` from 10 to 25 for complex workflows ### 📝 Agent Prompt Refinements Significantly simplified prompts for better clarity: - Workflow Builder: 51→36 lines - Helper: 49→28 lines - Data Manipulator: Enhanced with sorting guidance ### 🔍 Enhanced Debugging - Plan reasoning and step count in data message parts - Router debug info with token usage tracking - Better logging throughout execution pipeline ## Benefits 1. **Simpler Mental Model**: Router decides upfront vs dynamic transfers 2. **Better Predictability**: Users see the plan before execution 3. **Cleaner Architecture**: SRP with focused services 4. **Configuration Over Code**: Agent behavior via config, not hardcoded logic 5. **Plan Validation**: Catches invalid dependencies and cycles ## Migration Notes - Database migration removes `agentHandoff` table - Adds `plannerModel` column to workspace table - No API breaking changes (agent endpoints unchanged) ## Testing - Integration tests updated to remove handoff dependencies - Agent tool test utilities simplified - Plan validation covered by new logic ## Next Steps (Future PRs) - Parallel execution of independent plan steps - Dynamic re-planning based on results - Plan caching for common routing patterns - Error recovery strategies in plan executor |
||
|
|
59fbe35a8c |
Move view in metadata-modules/ and create atomic folder + module for each view entity (#14990)
# Introduction Preparing view-filter and view-group introduction in v2 core engine Moving view from `core-modules` to `metadata-modules` ## What happened ### Created dedicated modules for each view entity: - ViewFieldModule - ViewFilterModule - ViewFilterGroupModule - ViewGroupModule - ViewSortModule ### Each module is now completely independent with its own: - Controller - Resolver - Service - Entity ### Created dedicated abstraction metadata module folder for: - flat-view-field - flat-view ### Dependencies - Eleminated circular dep on ViewModule to all others ones - Granular import not importing the whole viewModule anymore everywhere close https://github.com/twentyhq/core-team-issues/issues/1703 |
||
|
|
0b60aa4249 |
Add custom routes to migration v2 (#14846)
## Context Add routes to migration V2 - Resolvers - Service v2 - Builder - Validator - Action runner Next PR: Add to twenty-cli to sync routes with serverless |
||
|
|
1938202780 |
1573 extensibility twenty cli handle custom layers for serverless functions of applications (#14779)
- allow specific layers for serverless functions - add a serverlessFunctionLayer table - sync application layer |
||
|
|
68b21d2942 |
Add db event trigger and cron trigger to migration v2 (#14772)
## Context Add DB events triggers and Cron triggers to migration v2 builder/runner (and adding corresponding services/resolvers) |
||
|
|
ff3467c18e |
feat (search): add dynamic search field metadata foundation for database-driven search configuration (#14469)
## Summary Implements the foundation for dynamic search field configuration from [issue #1428](https://github.com/twentyhq/core-team-issues/issues/1428). ## Changes - Add `SearchFieldMetadataEntity` junction table for storing searchable field configurations - Add `SearchFieldMetadataService` with core CRUD operations - Add `SearchFieldMetadataModule` following existing patterns - Add `IS_DYNAMIC_SEARCH_FIELDS_ENABLED` feature flag (defaults to `false`) - Database migration with proper indexes and foreign keys ## Architecture Uses a junction table where **record existence = field is searchable**: **Table: `searchFieldMetadata(objectMetadataId, fieldMetadataId, workspaceId)`** - Unique constraint on `(objectMetadataId, fieldMetadataId)` - `ON DELETE CASCADE` on foreign keys ## Testing - [x] Migration runs successfully - [x] Table created with correct schema - [x] Feature flag seeded properly - [x] Database reset works correctly ## Next Steps Future PRs will handle: - Data migration from existing hardcoded search field configs - Integration with search services ✅ No breaking changes — fully backward compatible. |
||
|
|
a722d97724 |
14066 extensibility add coretriggerroute table (#14241)
This Pr - adds a `route` table to the core schema - adds a controller to trigger route For now, we need to add a `/s/<workspace_id>` prefix to all routes We plan to create a custom domain table in order to let the users create subdomains for their workspace, and so to link their routes to a subdomain. Thank to that, we will be able to identify workspace_id from domain name, and the prefix could be `/s`. If we create a native dedicated subdomain for routes for each workspaces, the prefix could be completely removed! Here the follow up ticket to do that -> https://github.com/twentyhq/twenty/issues/14240 |
||
|
|
65df511179 |
feat: Add AI Agent workflow action node (#12650)
https://github.com/user-attachments/assets/8593e488-cb00-4fd2-b903-5ba5766e0254 --------- Co-authored-by: Antoine Moreaux <moreaux.antoine@gmail.com> Co-authored-by: martmull <martmull@hotmail.fr> Co-authored-by: Félix Malfait <felix.malfait@gmail.com> Co-authored-by: Baptiste Devessier <baptiste@devessier.fr> Co-authored-by: Joseph Chiang <josephj6802@gmail.com> Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Guillim <guillim@users.noreply.github.com> Co-authored-by: Raphaël Bosi <71827178+bosiraphael@users.noreply.github.com> Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com> Co-authored-by: Marie <51697796+ijreilly@users.noreply.github.com> Co-authored-by: Naifer <161821705+omarNaifer12@users.noreply.github.com> Co-authored-by: prastoin <paul@twenty.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions <github-actions@twenty.com> Co-authored-by: Thomas Trompette <thomas.trompette@sfr.fr> Co-authored-by: Etienne <45695613+etiennejouan@users.noreply.github.com> Co-authored-by: Ajay A Adsule <103304466+AjayAdsule@users.noreply.github.com> Co-authored-by: bosiraphael <raphael.bosi@gmail.com> Co-authored-by: Charles Bochet <charles@twenty.com> Co-authored-by: Marty <91310557+real-marty@users.noreply.github.com> Co-authored-by: Félix Malfait <felix@twenty.com> Co-authored-by: Charles Bochet <charlesBochet@users.noreply.github.com> Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Paul Rastoin <45004772+prastoin@users.noreply.github.com> Co-authored-by: Weiko <corentin@twenty.com> Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> Co-authored-by: nitin <142569587+ehconitin@users.noreply.github.com> |
||
|
|
a68895189c |
Deprecate old relations completely (#12482)
# What Fully deprecate old relations because we have one bug tied to it and it make the codebase complex # How I've made this PR: 1. remove metadata datasource (we only keep 'core') => this was causing extra complexity in the refactor + flaky reset 2. merge dev and demo datasets => as I needed to update the tests which is very painful, I don't want to do it twice 3. remove all code tied to RELATION_METADATA / relation-metadata.resolver, or anything tied to the old relation system 4. Remove ONE_TO_ONE and MANY_TO_MANY that are not supported 5. fix impacts on the different areas : see functional testing below # Functional testing ## Functional testing from the front-end: 1. Database Reset ✅ 2. Sign In ✅ 3. Workspace sign-up ✅ 5. Browsing table / kanban / show ✅ 6. Assigning a record in a one to many / in a many to one ✅ 7. Deleting a record involved in a relation ✅ => broken but not tied to this PR 8. "Add new" from relation picker ✅ => broken but not tied to this PR 9. Creating a Task / Note, Updating a Task / Note relations, Deleting a Task / Note (from table, show page, right drawer) ✅ => broken but not tied to this PR 10. creating a relation from settings (custom / standard x oneToMany / manyToOne) ✅ 11. updating a relation from settings should not be possible ✅ 12. deleting a relation from settings (custom / standard x oneToMany / manyToOne) ✅ 13. Make sure timeline activity still work (relation were involved there), espacially with Task / Note => to be double checked ✅ => Cannot convert undefined or null to object 14. Workspace deletion / User deletion ✅ 15. CSV Import should keep working ✅ 16. Permissions: I have tested without permissions V2 as it's still hard to test v2 work and it's not in prod yet ✅ 17. Workflows global test ✅ ## From the API: 1. Review open-api documentation (REST) ✅ 2. Make sure REST Api are still able to fetch relations ==> won't do, we have a coupling Get/Update/Create there, this requires refactoring 3. Make sure REST Api is still able to update / remove relation => won't do same ## Automated tests 1. lint + typescript ✅ 2. front unit tests: ✅ 3. server unit tests 2 ✅ 4. front stories: ✅ 5. server integration: ✅ 6. chromatic check : expected 0 7. e2e check : expected no more that current failures ## Remove // Todos 1. All are captured by functional tests above, nothing additional to do ## (Un)related regressions 1. Table loading state is not working anymore, we see the empty state before table content 2. Filtering by Creator Tim Ap return empty results 3. Not possible to add Tasks / Notes / Files from show page # Result ## New seeds that can be easily extended <img width="1920" alt="image" src="https://github.com/user-attachments/assets/d290d130-2a5f-44e6-b419-7e42a89eec4b" /> ## -5k lines of code ## No more 'metadata' dataSource (we only have 'core) ## No more relationMetadata (I haven't drop the table yet it's not referenced in the code anymore) ## We are ready to fix the 6 months lag between current API results and our mocked tests ## No more bug on relation creation / deletion --------- Co-authored-by: Weiko <corentin@twenty.com> Co-authored-by: Félix Malfait <felix@twenty.com> |
||
|
|
351e768038 |
[Permissions] Implement getRoles (#9955)
In this PR - introducing roles module to separate roles logic (assign a Role, get a workspace's roles etc.) from permission logic (check if a user has a permission) - Introduces getRoles endpoint to fetch a workspace's roles - introduces the first permission check: getRoles in only accessible to users with permission on ROLE setting. Implemented validatesUserHasWorkspaceSettingPermissionOrThrow |
||
|
|
17a1760afd |
Improve performance twenty orm (#6691)
## Context As we grow, the messaging scripts are experiencing performance issues forcing us to temporarily disable them on the cloud. While investigating the performance, I have noticed that generating the entity schema (for twentyORM) in the repository is taking ~500ms locally on my Mac M2 so likely more on pods. Caching the entitySchema then! I'm also clarifying naming around schemaVersion and cacheVersions ==> both are renamed workspaceMetadataVersion and migrated to the workspace table (the workspaceCacheVersion table is dropped). |
||
|
|
47ddc7be83 |
6181 workflows create a custom code executor (#6235)
Closes #6181 ## Testing - download Altair graphql dev tool https://altairgraphql.dev/#download - create a file locally `test.ts` containing: ``` export const handler = async (event: object, context: object) => { return { test: 'toto', data: event['data'] }; } ``` - play those requests in Altair: mutation UpsertFunction($file: Upload!) { upsertFunction(name: "toto", file: $file) } mutation ExecFunction { executeFunction(name:"toto", payload: {data: "titi"}) } - it will run the local driver, add those env variable to test with lambda driver ``` CUSTOM_CODE_ENGINE_DRIVER_TYPE=lambda LAMBDA_REGION=eu-west-2 LAMBDA_ROLE=<ASK_ME> ``` |
||
|
|
9e70f5b650 |
Add endpoints to create and delete remote server (#4606)
* Build remote server * Add getters * Migrate to json inputs * Use extendable type * Use regex validation * Remove acronymes --------- Co-authored-by: Thomas Trompette <thomast@twenty.com> |
||
|
|
e5c1309e8c |
feat: wip server folder structure (#4573)
* feat: wip server folder structure * fix: merge * fix: wrong merge * fix: remove unused file * fix: comment * fix: lint * fix: merge * fix: remove console.log * fix: metadata graphql arguments broken |