Add default relation to standard object on custom object in manifest (#18033)

add default relation fields when creating an object from an application

---------

Co-authored-by: prastoin <paul@twenty.com>
This commit is contained in:
martmull
2026-02-20 11:49:06 +01:00
committed by GitHub
parent d060c843d1
commit 3bc887e12e
48 changed files with 1812 additions and 554 deletions
@@ -0,0 +1 @@
export const API_CLIENT_DIR = 'api-client';
@@ -1,10 +1,10 @@
import { type SyncableEntityOptions } from '@/application/syncableEntityOptionsType';
import {
type FieldMetadataDefaultValue,
type FieldMetadataOptions,
type FieldMetadataType,
type FieldMetadataUniversalSettings,
type RelationAndMorphRelationFieldMetadataType,
FieldMetadataDefaultValue,
FieldMetadataOptions,
FieldMetadataType,
FieldMetadataUniversalSettings,
RelationAndMorphRelationFieldMetadataType
} from '@/types';
export type RegularFieldManifest<
@@ -25,20 +25,27 @@ export type RegularFieldManifest<
objectUniversalIdentifier: string;
};
// Both sides of a relation must be declared explicitly in the manifest.
// Relation-specific universal settings (relationType, onDelete, joinColumnName)
// are provided through the `universalSettings` field.
export type RelationFieldManifest<
T extends
RelationAndMorphRelationFieldMetadataType = RelationAndMorphRelationFieldMetadataType,
> = RegularFieldManifest<T> & {
> = Omit<RegularFieldManifest<T>, 'universalSettings' | 'type'> & {
type: T;
relationTargetFieldMetadataUniversalIdentifier: string;
relationTargetObjectMetadataUniversalIdentifier: string;
};
universalSettings: FieldMetadataUniversalSettings<T>;
} & ([T] extends [FieldMetadataType.MORPH_RELATION]
? {
morphId: string;
}
: {
morphId?: undefined;
});
export type FieldManifest<
T extends FieldMetadataType = Exclude<
FieldMetadataType,
RelationAndMorphRelationFieldMetadataType
>,
> = RegularFieldManifest<T> | RelationFieldManifest;
export type FieldManifest<T extends FieldMetadataType = FieldMetadataType> =
T extends RelationAndMorphRelationFieldMetadataType
? RelationFieldManifest<
Extract<T, RelationAndMorphRelationFieldMetadataType>
>
: RegularFieldManifest<
Exclude<T, RelationAndMorphRelationFieldMetadataType>
>;
@@ -13,6 +13,7 @@ export type {
} from './applicationType';
export type { ApplicationVariables } from './applicationVariablesType';
export type { AssetManifest } from './assetManifestType';
export { API_CLIENT_DIR } from './constants/ApiClientDirectory';
export { ASSETS_DIR } from './constants/AssetDirectory';
export { DEFAULT_API_KEY_NAME } from './constants/DefaultApiKeyName';
export { DEFAULT_API_URL_NAME } from './constants/DefaultApiUrlName';
@@ -1,9 +1,10 @@
import { type FieldManifest } from '@/application/fieldManifestType';
import type { FieldMetadataType } from '@/types';
type DistributiveOmit<T, K extends PropertyKey> = T extends unknown
? Omit<T, K>
: never;
export type ObjectFieldManifest<
T extends FieldMetadataType = Exclude<
FieldMetadataType,
FieldMetadataType.RELATION
>,
> = Omit<FieldManifest<T>, 'objectUniversalIdentifier'>;
T extends FieldMetadataType = FieldMetadataType,
> = DistributiveOmit<FieldManifest<T>, 'objectUniversalIdentifier'>;
@@ -1,4 +1,4 @@
import { addCustomSuffixIfIsReserved } from '@/metadata/add-custom-suffix-if-reserved.util';
import { addCustomSuffixIfIsReserved } from '@/metadata/utils/add-custom-suffix-if-reserved.util';
describe('addCustomSuffixIfIsReserved', () => {
it('should add Custom suffix for reserved name "event"', () => {
@@ -1,4 +1,4 @@
import { computeMetadataNameFromLabel } from '@/metadata/compute-metadata-name-from-label.util';
import { computeMetadataNameFromLabel } from '@/metadata/utils/compute-metadata-name-from-label.util';
describe('computeMetadataNameFromLabel', () => {
it('should convert a label to camelCase', () => {
@@ -1,3 +0,0 @@
import { type ALL_METADATA_NAME } from '@/metadata/all-metadata-name.constant';
export type AllMetadataName = keyof typeof ALL_METADATA_NAME;
@@ -0,0 +1,9 @@
import { type STANDARD_OBJECTS } from '@/metadata/constants/standard-object.constant';
export const DEFAULT_RELATIONS_OBJECTS_STANDARD_IDS = [
'timelineActivity',
'favorite',
'attachment',
'noteTarget',
'taskTarget',
] as const satisfies (keyof typeof STANDARD_OBJECTS)[];
@@ -1,4 +1,4 @@
export const CORE_OBJECT_NAMES = [
const CORE_OBJECT_NAMES = [
'approvedAccessDomain',
'approvedAccessDomains',
'appToken',
+9 -11
View File
@@ -7,23 +7,21 @@
* |___/
*/
export { addCustomSuffixIfIsReserved } from './add-custom-suffix-if-reserved.util';
export { ALL_METADATA_NAME } from './all-metadata-name.constant';
export type { AllMetadataName } from './all-metadata-name.type';
export { checkIfFieldIsImageIdentifier } from './check-if-field-is-image-identifier.util';
export {
DEFAULT_LABEL_IDENTIFIER_FIELD_NAME,
checkIfFieldIsLabelIdentifier,
} from './check-if-field-is-label-identifier.util';
export { computeMetadataNameFromLabel } from './compute-metadata-name-from-label.util';
export { ALL_METADATA_NAME } from './constants/all-metadata-name.constant';
export { DEFAULT_RELATIONS_OBJECTS_STANDARD_IDS } from './constants/default-relations-object-standard-ids.constant';
export { RESERVED_METADATA_NAME_KEYWORDS } from './constants/reserved-metadata-name-keywords.constant';
export { STANDARD_OBJECTS } from './constants/standard-object.constant';
export type { AllMetadataName } from './types/all-metadata-name.type';
export type {
FailedMetadataValidationError,
FailedMetadataValidation,
MetadataValidationErrorResponse,
} from './MetadataValidationError';
export { WorkspaceMigrationV2ExceptionCode } from './MetadataValidationError';
export {
CORE_OBJECT_NAMES,
RESERVED_METADATA_NAME_KEYWORDS,
} from './reserved-metadata-name-keywords.constant';
export { STANDARD_OBJECTS } from './standard-object.constant';
} from './types/MetadataValidationError';
export { WorkspaceMigrationV2ExceptionCode } from './types/MetadataValidationError';
export { addCustomSuffixIfIsReserved } from './utils/add-custom-suffix-if-reserved.util';
export { computeMetadataNameFromLabel } from './utils/compute-metadata-name-from-label.util';
@@ -1,4 +1,4 @@
import { type AllMetadataName } from './all-metadata-name.type';
import { type AllMetadataName } from '@/metadata/types/all-metadata-name.type';
export type FailedMetadataValidationError = {
code: string;
@@ -0,0 +1,3 @@
import { type ALL_METADATA_NAME } from '@/metadata/constants/all-metadata-name.constant';
export type AllMetadataName = keyof typeof ALL_METADATA_NAME;
@@ -1,4 +1,4 @@
import { RESERVED_METADATA_NAME_KEYWORDS } from './reserved-metadata-name-keywords.constant';
import { RESERVED_METADATA_NAME_KEYWORDS } from '@/metadata';
export const addCustomSuffixIfIsReserved = (name: string): string => {
if (!name) return name;
@@ -1,4 +1,4 @@
import { addCustomSuffixIfIsReserved } from '@/metadata/add-custom-suffix-if-reserved.util';
import { addCustomSuffixIfIsReserved } from '@/metadata/utils/add-custom-suffix-if-reserved.util';
import camelCase from 'lodash.camelcase';
import { slugify } from 'transliteration';