Update manifest structure (#17547)

Move all sync entities in an `entities` key. Rename functions to
logicFunctions

```json
{
  application: {
    ...
  },
  entities: {
    objects: [],
    logicFunctions: [],
    ...
  }
}
```
This commit is contained in:
martmull
2026-01-30 16:26:45 +01:00
committed by GitHub
parent bc022f82cb
commit f46da3eefd
162 changed files with 2555 additions and 3778 deletions
@@ -0,0 +1,242 @@
import { defineObject } from '@/sdk';
import { FieldMetadataType } from 'twenty-shared/types';
import { type ObjectManifest } from 'twenty-shared/application';
describe('defineObject', () => {
const validConfig: ObjectManifest = {
universalIdentifier: '54b589ca-eeed-4950-a176-358418b85c05',
nameSingular: 'postCard',
namePlural: 'postCards',
labelSingular: 'Post Card',
labelPlural: 'Post Cards',
icon: 'IconMail',
fields: [
{
universalIdentifier: '58a0a314-d7ea-4865-9850-7fb84e72f30b',
type: FieldMetadataType.TEXT,
name: 'content',
label: 'Content',
},
],
};
it('should return successful validation result when valid', () => {
const result = defineObject(validConfig);
expect(result.success).toBe(true);
expect(result.config).toEqual(validConfig);
expect(result.errors).toEqual([]);
});
it('should pass through all optional fields', () => {
const config: ObjectManifest = {
...validConfig,
description: 'A post card object',
};
const result = defineObject(config);
expect(result.success).toBe(true);
expect(result.config?.description).toBe('A post card object');
});
it('should return error when universalIdentifier is missing', () => {
const config = {
...validConfig,
universalIdentifier: undefined,
};
const result = defineObject(config as any);
expect(result.success).toBe(false);
expect(result.errors).toContain('Object must have a universalIdentifier');
});
it('should return error when nameSingular is missing', () => {
const config = {
...validConfig,
nameSingular: undefined,
};
const result = defineObject(config as any);
expect(result.success).toBe(false);
expect(result.errors).toContain('Object must have a nameSingular');
});
it('should return error when namePlural is missing', () => {
const config = {
...validConfig,
namePlural: undefined,
};
const result = defineObject(config as any);
expect(result.success).toBe(false);
expect(result.errors).toContain('Object must have a namePlural');
});
it('should return error when labelSingular is missing', () => {
const config = {
...validConfig,
labelSingular: undefined,
};
const result = defineObject(config as any);
expect(result.success).toBe(false);
expect(result.errors).toContain('Object must have a labelSingular');
});
it('should return error when labelPlural is missing', () => {
const config = {
...validConfig,
labelPlural: undefined,
};
const result = defineObject(config as any);
expect(result.success).toBe(false);
expect(result.errors).toContain('Object must have a labelPlural');
});
it('should accept empty fields array', () => {
const config = {
...validConfig,
fields: [],
};
const result = defineObject(config as any);
expect(result.config.fields).toEqual([]);
});
it('should accept missing fields', () => {
const config = {
...validConfig,
fields: undefined,
};
const result = defineObject(config as any);
expect(result.config.fields).toBeUndefined();
});
it('should return error when field is missing label', () => {
const config = {
...validConfig,
fields: [
{
universalIdentifier: '58a0a314-d7ea-4865-9850-7fb84e72f30b',
type: FieldMetadataType.TEXT,
name: 'content',
},
],
};
const result = defineObject(config as any);
expect(result.success).toBe(false);
expect(result.errors).toContain('Field must have a label');
});
it('should return error when field is missing name', () => {
const config = {
...validConfig,
fields: [
{
universalIdentifier: '58a0a314-d7ea-4865-9850-7fb84e72f30b',
type: FieldMetadataType.TEXT,
label: 'Content',
},
],
};
const result = defineObject(config as any);
expect(result.success).toBe(false);
expect(result.errors).toContain('Field "Content" must have a name');
});
it('should return error when field is missing universalIdentifier', () => {
const config = {
...validConfig,
fields: [
{
type: FieldMetadataType.TEXT,
name: 'content',
label: 'Content',
},
],
};
const result = defineObject(config as any);
expect(result.success).toBe(false);
expect(result.errors).toContain(
'Field "Content" must have a universalIdentifier',
);
});
it('should return error when SELECT field has no options', () => {
const config = {
...validConfig,
fields: [
{
universalIdentifier: '58a0a314-d7ea-4865-9850-7fb84e72f30b',
type: FieldMetadataType.SELECT,
label: 'Status',
name: 'status',
},
],
};
const result = defineObject(config as any);
expect(result.success).toBe(false);
expect(result.errors).toContain(
'Field "Status" is a SELECT/MULTI_SELECT type and must have options',
);
});
it('should return error when MULTI_SELECT field has no options', () => {
const config = {
...validConfig,
fields: [
{
universalIdentifier: '58a0a314-d7ea-4865-9850-7fb84e72f30b',
type: FieldMetadataType.MULTI_SELECT,
label: 'Tags',
name: 'tag',
},
],
};
const result = defineObject(config as any);
expect(result.success).toBe(false);
expect(result.errors).toContain(
'Field "Tags" is a SELECT/MULTI_SELECT type and must have options',
);
});
it('should accept SELECT field with options', () => {
const config = {
...validConfig,
fields: [
{
universalIdentifier: '58a0a314-d7ea-4865-9850-7fb84e72f30b',
type: FieldMetadataType.SELECT,
name: 'status',
label: 'Status',
options: [
{ value: 'draft', label: 'Draft', color: 'gray', position: 0 },
{ value: 'sent', label: 'Sent', color: 'green', position: 1 },
],
},
],
};
const result = defineObject(config as any);
expect(result.config.fields[0].options).toHaveLength(2);
});
});
@@ -0,0 +1,38 @@
import { type ObjectManifest } from 'twenty-shared/application';
import { createValidationResult } from '@/sdk/common/utils/create-validation-result';
import { type DefineEntity } from '@/sdk/common/types/define-entity.type';
import { validateFields } from '@/sdk/fields/validate-fields';
export const defineObject: DefineEntity<ObjectManifest> = (config) => {
const errors = [];
if (!config.universalIdentifier) {
errors.push('Object must have a universalIdentifier');
}
if (!config.nameSingular) {
errors.push('Object must have a nameSingular');
}
if (!config.namePlural) {
errors.push('Object must have a namePlural');
}
if (!config.labelSingular) {
errors.push('Object must have a labelSingular');
}
if (!config.labelPlural) {
errors.push('Object must have a labelPlural');
}
const fieldErrors = validateFields(config.fields);
errors.push(...fieldErrors);
return createValidationResult({
config,
errors,
});
};
@@ -0,0 +1 @@
export { STANDARD_OBJECT_IDS as STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS } from 'twenty-shared/metadata';