mirror of
https://github.com/twentyhq/twenty.git
synced 2026-09-16 07:56:04 -04:00
Follow-up to https://github.com/twentyhq/twenty/pull/24869, closes twentyhq/core-team-issues#2748. ## Why #24869 fixed `additionalEmails` typing by post-processing the generated client SDK: a static map of composite type/field names rewrote `Scalars['JSON']` into a hand-written TypeScript shape after codegen. That kept the wrong type in the schema itself and only patched one consumer. The root cause is the schema: composite sub-fields stored as `RAW_JSON` (`Emails.additionalEmails`, `Phones.additionalPhones`, `Links.secondaryLinks`, `Actor.context`) are exposed as the opaque `JSON` scalar even though their shape is known. ## What - Reverts #24869 (`composite-field-type-overrides.ts`, its tests and the genql engine snapshot test). - The workspace schema builder now maps those four sub-fields to real GraphQL types instead of the JSON scalar: - `additionalEmails: [String!]` - `additionalPhones: [AdditionalPhone!]` / `[AdditionalPhoneInput!]` (`number`, `callingCode`, `countryCode`) - `secondaryLinks: [SecondaryLink!]` / `[SecondaryLinkInput!]` (`label`, `url`) - `context: ActorContext` / `ActorContextInput` (`provider`) Same pattern as the existing `FILES` field type, which already resolves a jsonb column through `FileObject`. Nested fields are nullable so partially filled rows stay readable. Filter, order-by and group-by inputs are untouched: they never expose the raw value. - Direct execution returned the stored value as-is for composite sub-fields, so a nested selection got back the raw jsonb without `__typename`. `graphQLFormatResultFromSelectedFields` now projects the selected sub-fields of an object-typed composite sub-field and fills in its `__typename`; a value that is not an object (a legacy JSON string, say) still passes through untouched. - Front: composite selections in `mapFieldMetadataToGraphQLQuery` request the nested sub-fields, and `sanitizeRecordInput` strips `__typename` from composite values before sending them as mutation input (Apollo adds it to every nested selection; composite input types do not accept it). Optimistic cache writes for `LINKS` carry the nested `__typename`. - Server/e2e test gql field constants updated to the nested selections. Generated client types, straight from the schema now: ```ts additionalEmails?: string[] additionalPhones?: AdditionalPhone[] secondaryLinks?: SecondaryLink[] context?: ActorContext ``` ## Compatibility The workspace API runs through direct execution, which does not validate documents or variables against the generated schema, so existing queries selecting `additionalPhones` / `secondaryLinks` / `createdBy.context` as leaves keep working and keep returning the stored value, and mutations still accept what they accepted before. What changes is the published schema: introspection, SDL and anything generated or validated from them (the client SDK above, schema-aware tooling) now see object types where they saw `JSON`. `api-breaking-changes` diffs the introspected schema and counts a field type change as breaking, so it fails on this PR by design. Keeping it green would mean leaving the JSON fields in place and adding parallel typed ones, which is the opposite of the intent here — flagging it for reviewers rather than working around it. ## Test - Unit test asserting the composite output and create-input types render as the real types. - Unit test for the direct-execution formatter: nested sub-field projection with `__typename`, and passthrough for a non-object value. - `twenty-front` and `twenty-server` unit suites pass (remaining failures in this environment are unbuilt sibling packages: `twenty-emails`, `twenty-client-sdk/generate`, `twenty-front-component-renderer`). - Generated a client from a schema with the new composite types and checked the emitted TypeScript matches the shapes above. - Integration and e2e suites were not run locally (no database); the phone metadata suite's expectations were corrected from the CI run.